@adhdev/daemon-standalone 0.9.82-rc.261 → 0.9.82-rc.263

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/index.js CHANGED
@@ -29965,6 +29965,27 @@ var require_dist3 = __commonJS({
29965
29965
  };
29966
29966
  }
29967
29967
  });
29968
+ function readInjected(value) {
29969
+ if (typeof value !== "string") return void 0;
29970
+ const trimmed = value.trim();
29971
+ if (!trimmed || trimmed === "unknown") return void 0;
29972
+ return trimmed;
29973
+ }
29974
+ function getDaemonBuildInfo() {
29975
+ if (cached2) return cached2;
29976
+ const commit = readInjected(true ? "2cf602f99ce68b3d3f1af02b429f6fb7a7c8e686" : void 0) ?? "unknown";
29977
+ const commitShort = readInjected(true ? "2cf602f" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
29978
+ const version2 = readInjected(true ? "0.9.82-rc.263" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
29979
+ const builtAt = readInjected(true ? "2026-06-14T13:49:22.236Z" : void 0);
29980
+ cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
29981
+ return cached2;
29982
+ }
29983
+ var cached2;
29984
+ var init_build_info = __esm2({
29985
+ "src/build-info.ts"() {
29986
+ "use strict";
29987
+ }
29988
+ });
29968
29989
  async function getGitRepoStatus(workspace, options = {}) {
29969
29990
  const lastCheckedAt = Date.now();
29970
29991
  const includeSubmodules = options.includeSubmodules !== false;
@@ -29986,6 +30007,7 @@ var require_dist3 = __commonJS({
29986
30007
  }
29987
30008
  const submoduleDirty = (submodules || []).some((submodule) => submodule.dirty || submodule.outOfSync || !!submodule.error);
29988
30009
  const dirty = parsed.staged + parsed.modified + parsed.untracked + parsed.deleted + parsed.renamed > 0 || parsed.conflictFiles.length > 0 || stashCount > 0 || submoduleDirty;
30010
+ const daemonBuildBehind = await detectDaemonBuildBehind(repo, submodules, options);
29989
30011
  return {
29990
30012
  workspace: repo.workspace,
29991
30013
  repoRoot: repo.repoRoot,
@@ -30009,7 +30031,8 @@ var require_dist3 = __commonJS({
30009
30031
  conflictFiles: parsed.conflictFiles,
30010
30032
  stashCount,
30011
30033
  lastCheckedAt,
30012
- submodules
30034
+ submodules,
30035
+ ...daemonBuildBehind ? { daemonBuildBehind } : {}
30013
30036
  };
30014
30037
  } catch (error48) {
30015
30038
  if (error48 instanceof GitCommandError) {
@@ -30022,6 +30045,35 @@ var require_dist3 = __commonJS({
30022
30045
  );
30023
30046
  }
30024
30047
  }
30048
+ async function detectDaemonBuildBehind(repo, submodules, options) {
30049
+ const build = options.daemonBuildInfo ?? getDaemonBuildInfo();
30050
+ if (!build.commit || build.commit === "unknown") return void 0;
30051
+ const scopes = [
30052
+ { scope: "root", repoPath: repo.repoRoot || repo.workspace }
30053
+ ];
30054
+ for (const sub of submodules || []) {
30055
+ if (sub.repoPath && !sub.error) scopes.push({ scope: sub.path, repoPath: sub.repoPath });
30056
+ }
30057
+ for (const { scope, repoPath } of scopes) {
30058
+ try {
30059
+ await runGit(repoPath, ["cat-file", "-e", `${build.commit}^{commit}`], options);
30060
+ const headResult = await runGit(repoPath, ["rev-parse", "HEAD"], options);
30061
+ const head = headResult.stdout.trim();
30062
+ if (!head || head === build.commit) continue;
30063
+ await runGit(repoPath, ["merge-base", "--is-ancestor", build.commit, "HEAD"], options);
30064
+ return {
30065
+ buildCommit: build.commit,
30066
+ buildCommitShort: build.commitShort,
30067
+ head,
30068
+ scope,
30069
+ warning: `Live daemon was built from ${build.commitShort} which is behind ${scope === "root" ? "workspace" : scope} HEAD ${head.slice(0, 7)}. Merged code is NOT live until the daemon is rebuilt/redeployed and restarted \u2014 a local dist rebuild alone does not update a cloud daemon.`
30070
+ };
30071
+ } catch {
30072
+ continue;
30073
+ }
30074
+ }
30075
+ return void 0;
30076
+ }
30025
30077
  async function readPorcelainStatus(repo, options) {
30026
30078
  const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
30027
30079
  return parsePorcelainV2Status(statusOutput.stdout);
@@ -30238,6 +30290,7 @@ var require_dist3 = __commonJS({
30238
30290
  "src/git/git-status.ts"() {
30239
30291
  "use strict";
30240
30292
  init_git_executor();
30293
+ init_build_info();
30241
30294
  }
30242
30295
  });
30243
30296
  var git_diff_exports = {};
@@ -32106,8 +32159,8 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32106
32159
  }
32107
32160
  function getCachedRawEntries(meshId) {
32108
32161
  const now = Date.now();
32109
- const cached2 = ledgerReadCache.get(meshId);
32110
- if (cached2 && now - cached2.cachedAt < LEDGER_CACHE_TTL_MS) return cached2.entries;
32162
+ const cached22 = ledgerReadCache.get(meshId);
32163
+ if (cached22 && now - cached22.cachedAt < LEDGER_CACHE_TTL_MS) return cached22.entries;
32111
32164
  let entries;
32112
32165
  try {
32113
32166
  entries = readLedgerFromStore(meshId);
@@ -32384,6 +32437,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32384
32437
  cancelTask: () => cancelTask,
32385
32438
  claimNextTask: () => claimNextTask,
32386
32439
  cleanupTerminalDirectDispatches: () => cleanupTerminalDirectDispatches,
32440
+ deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
32387
32441
  describeTaskDependencyState: () => describeTaskDependencyState,
32388
32442
  enqueueTask: () => enqueueTask,
32389
32443
  getActiveDirectDispatches: () => getActiveDirectDispatches,
@@ -32792,6 +32846,13 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
32792
32846
  } catch {
32793
32847
  }
32794
32848
  }
32849
+ function deleteDirectDispatchesByTaskId(meshId, taskIds) {
32850
+ try {
32851
+ return MeshRuntimeStore.getInstance().deleteDirectDispatchesByTaskId(meshId, taskIds);
32852
+ } catch {
32853
+ return 0;
32854
+ }
32855
+ }
32795
32856
  function recordMeshToolCall(opts) {
32796
32857
  try {
32797
32858
  return MeshRuntimeStore.getInstance().recordMeshToolCall(opts);
@@ -33435,6 +33496,24 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
33435
33496
  deleteDirectDispatches(meshId) {
33436
33497
  this.db.prepare(`DELETE FROM mesh_direct_dispatches WHERE mesh_id = ?`).run(meshId);
33437
33498
  }
33499
+ /**
33500
+ * Delete specific direct dispatch rows by taskId for a mesh. Used by the staleDirect prune
33501
+ * path to remove orphaned/terminal dispatch records whose node/session is no longer in the
33502
+ * live mesh. Returns the number of rows actually deleted. No-op for an empty taskId list.
33503
+ */
33504
+ deleteDirectDispatchesByTaskId(meshId, taskIds) {
33505
+ const ids = (taskIds || []).map((id) => typeof id === "string" ? id.trim() : "").filter(Boolean);
33506
+ if (!ids.length) return 0;
33507
+ const stmt = this.db.prepare(`DELETE FROM mesh_direct_dispatches WHERE mesh_id = ? AND task_id = ?`);
33508
+ let deleted = 0;
33509
+ const run = this.db.transaction((rows) => {
33510
+ for (const taskId of rows) {
33511
+ deleted += stmt.run(meshId, taskId).changes;
33512
+ }
33513
+ });
33514
+ run(ids);
33515
+ return deleted;
33516
+ }
33438
33517
  markStaleDirectDispatches(meshId, olderThanMs) {
33439
33518
  const cutoff = new Date(Date.now() - olderThanMs).toISOString();
33440
33519
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -35121,11 +35200,14 @@ ${rendered}`, "utf-8");
35121
35200
  const trigger = normalizeOptionalString(args.trigger) || "manual";
35122
35201
  const updateSubmodules = args.updateSubmodules === true;
35123
35202
  const dryRun = args.dryRun === true || args.execute !== true;
35124
- const plannedSteps = buildPlannedSteps(updateSubmodules);
35203
+ const mode = args.mode === "push" ? "push" : "merge";
35204
+ const pushSubmodules = mode === "push" && args.pushSubmodules === true;
35205
+ const plannedSteps = buildPlannedSteps(mode, updateSubmodules, pushSubmodules);
35125
35206
  const base = {
35126
35207
  ...nodeId ? { nodeId } : {},
35127
35208
  ...meshId ? { meshId } : {},
35128
35209
  workspace,
35210
+ mode,
35129
35211
  dryRun,
35130
35212
  updateSubmodules,
35131
35213
  plannedSteps,
@@ -35139,13 +35221,24 @@ ${rendered}`, "utf-8");
35139
35221
  submoduleIgnorePaths: args.submoduleIgnorePaths,
35140
35222
  timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
35141
35223
  });
35224
+ if (mode === "push") {
35225
+ return pushMeshNode(base, args, current, {
35226
+ pushSubmodules,
35227
+ allowAutoPublishSubmoduleMainCommits: args.allowAutoPublishSubmoduleMainCommits === true
35228
+ });
35229
+ }
35142
35230
  const earlyBlockers = collectPreflightBlockers(current, requestedBranch);
35143
35231
  if (earlyBlockers.length > 0) {
35232
+ const blockCode = chooseBlockCode(current, earlyBlockers);
35144
35233
  const result2 = {
35145
- ...block(base, chooseBlockCode(current, earlyBlockers), earlyBlockers),
35234
+ ...block(base, blockCode, earlyBlockers),
35146
35235
  current,
35147
- finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(chooseBlockCode(current, earlyBlockers)))
35236
+ finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(blockCode))
35148
35237
  };
35238
+ if (blockCode === "branch_ahead" && current.ahead > 0 && current.behind === 0 && otherBlockersAreOnlyAhead(earlyBlockers)) {
35239
+ result2.code = "ahead_needs_push";
35240
+ result2.nextStep = 'Local branch is ahead of origin with nothing to merge. Re-run mesh_fast_forward_node with mode="push" (execute=true) to ff-only push the local commits to origin.';
35241
+ }
35149
35242
  await appendFastForwardLedger(result2, "blocked");
35150
35243
  return result2;
35151
35244
  }
@@ -35256,7 +35349,246 @@ ${rendered}`, "utf-8");
35256
35349
  await appendFastForwardLedger(result, success2 ? "executed" : "failed");
35257
35350
  return result;
35258
35351
  }
35259
- function buildPlannedSteps(updateSubmodules) {
35352
+ async function pushMeshNode(base, args, current, options) {
35353
+ const workspace = base.workspace;
35354
+ const requestedBranch = normalizeOptionalString(args.branch);
35355
+ const dryRun = base.dryRun;
35356
+ const blockers = collectPushPreflightBlockers(current, requestedBranch);
35357
+ if (blockers.length > 0) {
35358
+ const code2 = choosePushBlockCode(current, blockers);
35359
+ const result2 = {
35360
+ ...block(base, code2, blockers),
35361
+ current,
35362
+ preStatus: current,
35363
+ finalBranchConvergenceState: buildConvergenceState(current, codeToConvergenceStatus(code2))
35364
+ };
35365
+ await appendFastForwardLedger(result2, "blocked");
35366
+ return result2;
35367
+ }
35368
+ const target = parseUpstreamTarget(current.upstream || "");
35369
+ if (!target) {
35370
+ const result2 = {
35371
+ ...block(base, "upstream_unparseable", ["upstream_unparseable"]),
35372
+ current,
35373
+ preStatus: current,
35374
+ finalBranchConvergenceState: buildConvergenceState(current, "blocked")
35375
+ };
35376
+ await appendFastForwardLedger(result2, "blocked");
35377
+ return result2;
35378
+ }
35379
+ const refspec = `HEAD:refs/heads/${target.remoteBranch}`;
35380
+ const pushTarget = { remote: target.remote, remoteBranch: target.remoteBranch, refspec };
35381
+ if (current.ahead <= 0) {
35382
+ const result2 = {
35383
+ ...base,
35384
+ success: true,
35385
+ code: "nothing_to_push",
35386
+ allowed: true,
35387
+ willRun: false,
35388
+ executed: false,
35389
+ blockingReasons: [],
35390
+ current,
35391
+ preStatus: current,
35392
+ postStatus: current,
35393
+ pushTarget,
35394
+ finalBranchConvergenceState: buildConvergenceState(current, "up_to_date")
35395
+ };
35396
+ await appendFastForwardLedger(result2, "noop");
35397
+ return result2;
35398
+ }
35399
+ const descendant = await verifyUpstreamIsAncestorOfHead(workspace, current.upstream || "", args.timeoutMs);
35400
+ if (!descendant.ok) {
35401
+ const result2 = {
35402
+ ...block(base, "non_fast_forward_push", ["head_is_not_descendant_of_upstream"]),
35403
+ current,
35404
+ preStatus: current,
35405
+ pushTarget,
35406
+ operationError: descendant.error,
35407
+ nextStep: "origin/<branch> has commits not in local HEAD; a ff-only push would lose them. Converge by rebasing onto origin first, then re-run. This operation never force-pushes.",
35408
+ finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
35409
+ };
35410
+ await appendFastForwardLedger(result2, "blocked");
35411
+ return result2;
35412
+ }
35413
+ if (dryRun) {
35414
+ const result2 = {
35415
+ ...base,
35416
+ success: true,
35417
+ code: "push_available",
35418
+ allowed: true,
35419
+ willRun: false,
35420
+ executed: false,
35421
+ blockingReasons: [],
35422
+ current,
35423
+ preStatus: current,
35424
+ pushTarget,
35425
+ ...options.pushSubmodules ? { submodulePushes: await planSubmodulePushes(current, options, args.timeoutMs) } : {},
35426
+ finalBranchConvergenceState: buildConvergenceState(current, "push_available")
35427
+ };
35428
+ await appendFastForwardLedger(result2, "dry_run");
35429
+ return result2;
35430
+ }
35431
+ try {
35432
+ await runGit(workspace, ["push", target.remote, refspec], { timeoutMs: args.timeoutMs ?? 3e4 });
35433
+ } catch (error48) {
35434
+ const result2 = {
35435
+ ...block(base, "push_ff_only_failed", ["push_ff_only_failed"]),
35436
+ current,
35437
+ preStatus: current,
35438
+ pushTarget,
35439
+ operationError: formatGitError2(error48),
35440
+ finalBranchConvergenceState: buildConvergenceState(current, "not_mergeable")
35441
+ };
35442
+ await appendFastForwardLedger(result2, "failed");
35443
+ return result2;
35444
+ }
35445
+ let submodulePushes;
35446
+ if (options.pushSubmodules) {
35447
+ submodulePushes = await executeSubmodulePushes(current, options, args.timeoutMs);
35448
+ }
35449
+ const postStatus = await getGitRepoStatus(workspace, {
35450
+ ...STATUS_OPTIONS,
35451
+ submoduleIgnorePaths: args.submoduleIgnorePaths,
35452
+ timeoutMs: args.timeoutMs ?? STATUS_OPTIONS.timeoutMs
35453
+ });
35454
+ const submodulePushFailed = (submodulePushes || []).some((entry) => !entry.pushed && !entry.skipped);
35455
+ const blockingReasons = [];
35456
+ if (postStatus.ahead !== 0) blockingReasons.push("post_branch_ahead");
35457
+ if (submodulePushFailed) blockingReasons.push("submodule_push_failed");
35458
+ const success2 = blockingReasons.length === 0;
35459
+ const code = success2 ? "push_applied" : submodulePushFailed && postStatus.ahead === 0 ? "push_applied_submodule_push_failed" : "post_push_verify_failed";
35460
+ const result = {
35461
+ ...base,
35462
+ success: success2,
35463
+ code,
35464
+ allowed: true,
35465
+ willRun: true,
35466
+ executed: true,
35467
+ blockingReasons,
35468
+ current,
35469
+ preStatus: current,
35470
+ postStatus,
35471
+ pushTarget,
35472
+ ...submodulePushes ? { submodulePushes } : {},
35473
+ finalBranchConvergenceState: buildConvergenceState(postStatus, success2 ? "pushed" : "post_verify_failed")
35474
+ };
35475
+ await appendFastForwardLedger(result, success2 ? "executed" : "failed");
35476
+ return result;
35477
+ }
35478
+ function collectPushPreflightBlockers(status, requestedBranch) {
35479
+ const blockers = [];
35480
+ if (!status.isGitRepo) blockers.push("not_git_repo");
35481
+ if (!status.branch) blockers.push("detached_head_or_unknown_branch");
35482
+ if (requestedBranch && status.branch !== requestedBranch) blockers.push("branch_mismatch");
35483
+ if (!status.upstream) blockers.push("upstream_missing");
35484
+ if (status.upstreamStatus !== "fresh") blockers.push("upstream_not_fresh");
35485
+ if (status.hasConflicts) blockers.push("conflicts_present");
35486
+ if (status.staged > 0) blockers.push("staged_changes_present");
35487
+ if (status.modified > 0) blockers.push("modified_changes_present");
35488
+ if (status.untracked > 0) blockers.push("untracked_changes_present");
35489
+ if (status.deleted > 0) blockers.push("deleted_changes_present");
35490
+ if (status.renamed > 0) blockers.push("renamed_changes_present");
35491
+ if (status.stashCount > 0) blockers.push("stash_entries_present");
35492
+ if (status.ahead > 0 && status.behind > 0) blockers.push("branch_diverged_from_upstream");
35493
+ else if (status.behind > 0) blockers.push("branch_behind_upstream");
35494
+ return blockers;
35495
+ }
35496
+ function choosePushBlockCode(status, blockers) {
35497
+ if (blockers.includes("not_git_repo")) return "not_git_repo";
35498
+ if (blockers.includes("branch_mismatch")) return "branch_mismatch";
35499
+ if (blockers.includes("upstream_missing")) return "upstream_missing";
35500
+ if (blockers.includes("upstream_not_fresh")) return "upstream_not_fresh";
35501
+ if (blockers.includes("branch_diverged_from_upstream")) return "branch_diverged";
35502
+ if (blockers.includes("branch_behind_upstream")) return "non_fast_forward_push";
35503
+ if (blockers.some((reason) => reason.includes("changes") || reason.includes("conflicts") || reason.includes("stash"))) return "dirty_worktree";
35504
+ return "preflight_blocked";
35505
+ }
35506
+ function parseUpstreamTarget(upstream) {
35507
+ const trimmed = upstream.trim();
35508
+ const slash = trimmed.indexOf("/");
35509
+ if (slash <= 0 || slash >= trimmed.length - 1) return null;
35510
+ return { remote: trimmed.slice(0, slash), remoteBranch: trimmed.slice(slash + 1) };
35511
+ }
35512
+ async function verifyUpstreamIsAncestorOfHead(workspace, upstream, timeoutMs) {
35513
+ if (!upstream) return { ok: false, error: "missing upstream" };
35514
+ try {
35515
+ await runGit(workspace, ["merge-base", "--is-ancestor", upstream, "HEAD"], { timeoutMs: timeoutMs ?? 15e3 });
35516
+ return { ok: true };
35517
+ } catch (error48) {
35518
+ return { ok: false, error: formatGitError2(error48) };
35519
+ }
35520
+ }
35521
+ async function planSubmodulePushes(status, options, timeoutMs) {
35522
+ return resolveSubmodulePushes(status, options, false, timeoutMs);
35523
+ }
35524
+ async function executeSubmodulePushes(status, options, timeoutMs) {
35525
+ return resolveSubmodulePushes(status, options, true, timeoutMs);
35526
+ }
35527
+ async function resolveSubmodulePushes(status, options, execute, timeoutMs) {
35528
+ const submodules = Array.isArray(status.submodules) ? status.submodules : [];
35529
+ const results = [];
35530
+ for (const submodule of submodules) {
35531
+ const base = {
35532
+ path: submodule.path,
35533
+ commit: submodule.commit,
35534
+ remote: "origin",
35535
+ remoteBranch: "main",
35536
+ pushed: false,
35537
+ skipped: true,
35538
+ code: "submodule_push_skipped"
35539
+ };
35540
+ if (!options.allowAutoPublishSubmoduleMainCommits) {
35541
+ results.push({ ...base, code: "submodule_push_policy_disabled", error: "allowAutoPublishSubmoduleMainCommits is not enabled" });
35542
+ continue;
35543
+ }
35544
+ if (submodule.error || submodule.dirty) {
35545
+ results.push({ ...base, code: "submodule_not_clean", error: submodule.error || "submodule worktree is dirty" });
35546
+ continue;
35547
+ }
35548
+ const repoPath = submodule.repoPath;
35549
+ if (!repoPath || !submodule.commit) {
35550
+ results.push({ ...base, code: "submodule_status_incomplete" });
35551
+ continue;
35552
+ }
35553
+ try {
35554
+ await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", "refs/heads/main:refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 3e4 });
35555
+ } catch (error48) {
35556
+ results.push({ ...base, code: "submodule_fetch_failed", error: formatGitError2(error48) });
35557
+ continue;
35558
+ }
35559
+ let alreadyReachable = false;
35560
+ try {
35561
+ await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, "refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 15e3 });
35562
+ alreadyReachable = true;
35563
+ } catch {
35564
+ }
35565
+ if (alreadyReachable) {
35566
+ results.push({ ...base, pushed: false, skipped: true, code: "submodule_already_reachable" });
35567
+ continue;
35568
+ }
35569
+ try {
35570
+ await runGit(repoPath, ["merge-base", "--is-ancestor", "refs/remotes/origin/main", submodule.commit], { timeoutMs: timeoutMs ?? 15e3 });
35571
+ } catch (error48) {
35572
+ results.push({ ...base, pushed: false, skipped: false, code: "submodule_non_fast_forward", error: formatGitError2(error48) });
35573
+ continue;
35574
+ }
35575
+ const refspec = `${submodule.commit}:refs/heads/main`;
35576
+ if (!execute) {
35577
+ results.push({ ...base, pushed: false, skipped: false, code: "submodule_push_available", refspec });
35578
+ continue;
35579
+ }
35580
+ try {
35581
+ await runGit(repoPath, ["push", "origin", refspec], { timeoutMs: timeoutMs ?? 3e4 });
35582
+ await runGit(repoPath, ["-c", "protocol.file.allow=always", "fetch", "origin", "refs/heads/main:refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 3e4 });
35583
+ await runGit(repoPath, ["merge-base", "--is-ancestor", submodule.commit, "refs/remotes/origin/main"], { timeoutMs: timeoutMs ?? 15e3 });
35584
+ results.push({ ...base, pushed: true, skipped: false, code: "submodule_pushed", refspec });
35585
+ } catch (error48) {
35586
+ results.push({ ...base, pushed: false, skipped: false, code: "submodule_push_failed", refspec, error: formatGitError2(error48) });
35587
+ }
35588
+ }
35589
+ return results;
35590
+ }
35591
+ function buildPlannedSteps(mode, updateSubmodules, pushSubmodules) {
35260
35592
  const steps = [
35261
35593
  {
35262
35594
  operation: "refresh_upstream",
@@ -35269,20 +35601,49 @@ ${rendered}`, "utf-8");
35269
35601
  description: "Require clean staged/modified/untracked/deleted/renamed/conflict/stash/submodule state.",
35270
35602
  safe: true,
35271
35603
  willMutateWorktree: false
35272
- },
35273
- {
35274
- operation: "verify_fast_forward",
35275
- description: "Require ahead=0, behind>0, and HEAD to be an ancestor of the upstream ref.",
35604
+ }
35605
+ ];
35606
+ if (mode === "push") {
35607
+ steps.push({
35608
+ operation: "verify_push_descendant",
35609
+ description: "Require HEAD to be a descendant of origin/<branch> (origin/<branch> is an ancestor of HEAD); refuse any non-fast-forward push.",
35276
35610
  safe: true,
35277
35611
  willMutateWorktree: false
35278
- },
35279
- {
35280
- operation: "merge_ff_only",
35281
- description: "Apply git merge --ff-only against the tracked upstream; no force, reset, rebase, push, or deploy.",
35612
+ });
35613
+ steps.push({
35614
+ operation: "push_ff_only",
35615
+ description: "Run git push origin HEAD:<branch> as a strict ff-only push; never --force, --force-with-lease, reset, or rebase. Does not mutate the worktree.",
35282
35616
  safe: true,
35283
- willMutateWorktree: true
35617
+ willMutateWorktree: false
35618
+ });
35619
+ if (pushSubmodules) {
35620
+ steps.push({
35621
+ operation: "push_submodules_ff_only",
35622
+ description: "For each submodule, if allowAutoPublishSubmoduleMainCommits is enabled and the submodule HEAD is a descendant of its origin main, ff-only push it to submodule origin main; otherwise skip.",
35623
+ safe: true,
35624
+ willMutateWorktree: false
35625
+ });
35284
35626
  }
35285
- ];
35627
+ steps.push({
35628
+ operation: "verify_post_status",
35629
+ description: "Re-read daemon-owned git status and report final branch convergence state.",
35630
+ safe: true,
35631
+ willMutateWorktree: false
35632
+ });
35633
+ return steps;
35634
+ }
35635
+ steps.push({
35636
+ operation: "verify_fast_forward",
35637
+ description: "Require ahead=0, behind>0, and HEAD to be an ancestor of the upstream ref.",
35638
+ safe: true,
35639
+ willMutateWorktree: false
35640
+ });
35641
+ steps.push({
35642
+ operation: "merge_ff_only",
35643
+ description: "Apply git merge --ff-only against the tracked upstream; no force, reset, rebase, push, or deploy.",
35644
+ safe: true,
35645
+ willMutateWorktree: true
35646
+ });
35286
35647
  if (updateSubmodules) {
35287
35648
  steps.push({
35288
35649
  operation: "submodule_update",
@@ -35299,6 +35660,10 @@ ${rendered}`, "utf-8");
35299
35660
  });
35300
35661
  return steps;
35301
35662
  }
35663
+ function otherBlockersAreOnlyAhead(blockers) {
35664
+ const aheadOnly = /* @__PURE__ */ new Set(["branch_has_local_commits"]);
35665
+ return blockers.every((reason) => aheadOnly.has(reason));
35666
+ }
35302
35667
  function collectPreflightBlockers(status, requestedBranch) {
35303
35668
  const blockers = [];
35304
35669
  if (!status.isGitRepo) blockers.push("not_git_repo");
@@ -35355,7 +35720,7 @@ ${rendered}`, "utf-8");
35355
35720
  return "preflight_blocked";
35356
35721
  }
35357
35722
  function codeToConvergenceStatus(code) {
35358
- if (code === "branch_diverged" || code === "branch_ahead" || code === "non_fast_forward") return "not_mergeable";
35723
+ if (code === "branch_diverged" || code === "branch_ahead" || code === "non_fast_forward" || code === "non_fast_forward_push" || code === "upstream_unparseable") return "not_mergeable";
35359
35724
  if (code === "dirty_worktree" || code === "submodule_not_clean") return "blocked_review";
35360
35725
  return "blocked";
35361
35726
  }
@@ -35438,6 +35803,7 @@ ${rendered}`, "utf-8");
35438
35803
  ...result.nodeId ? { nodeId: result.nodeId } : {},
35439
35804
  payload: {
35440
35805
  operation: "mesh_fast_forward_node",
35806
+ mode: result.mode,
35441
35807
  trigger: result.trigger || "manual",
35442
35808
  outcome,
35443
35809
  code: result.code,
@@ -35448,6 +35814,7 @@ ${rendered}`, "utf-8");
35448
35814
  executed: result.executed,
35449
35815
  branch: result.postStatus?.branch ?? result.current?.branch,
35450
35816
  upstream: result.postStatus?.upstream ?? result.current?.upstream,
35817
+ ...result.pushTarget ? { pushTarget: result.pushTarget } : {},
35451
35818
  before: result.current ? {
35452
35819
  headCommit: result.current.headCommit,
35453
35820
  ahead: result.current.ahead,
@@ -35458,6 +35825,16 @@ ${rendered}`, "utf-8");
35458
35825
  ahead: result.postStatus.ahead,
35459
35826
  behind: result.postStatus.behind
35460
35827
  } : void 0,
35828
+ ...result.submodulePushes ? {
35829
+ submodulePushes: result.submodulePushes.map((entry) => ({
35830
+ path: entry.path,
35831
+ commit: entry.commit,
35832
+ pushed: entry.pushed,
35833
+ skipped: entry.skipped,
35834
+ code: entry.code,
35835
+ ...entry.refspec ? { refspec: entry.refspec } : {}
35836
+ }))
35837
+ } : {},
35461
35838
  blockingReasons: result.blockingReasons
35462
35839
  }
35463
35840
  });
@@ -36628,8 +37005,8 @@ Next step: ${nextStep}`;
36628
37005
  });
36629
37006
  function getCachedMeshByWorkspace(workspace) {
36630
37007
  const now = Date.now();
36631
- const cached2 = meshByWorkspaceCache.get(workspace);
36632
- if (cached2 && now - cached2.cachedAt < MESH_WORKSPACE_CACHE_TTL_MS) return cached2.mesh;
37008
+ const cached22 = meshByWorkspaceCache.get(workspace);
37009
+ if (cached22 && now - cached22.cachedAt < MESH_WORKSPACE_CACHE_TTL_MS) return cached22.mesh;
36633
37010
  const mesh = getMeshByRepo(workspace);
36634
37011
  meshByWorkspaceCache.set(workspace, { mesh, cachedAt: now });
36635
37012
  return mesh;
@@ -41528,10 +41905,10 @@ ${lastSnapshot}`;
41528
41905
  return this.accumulatedRawBuffer.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, "").replace(/\x1B[P^_X][\s\S]*?(?:\x07|\x1B\\)/g, "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
41529
41906
  }
41530
41907
  getFreshParsedStatusCache() {
41531
- const cached2 = this.parsedStatusCache;
41908
+ const cached22 = this.parsedStatusCache;
41532
41909
  const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
41533
- if (cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.engine.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === this.lastScreenText && cached2.currentStatus === this.engine.currentStatus && cached2.activeModal === this.engine.activeModal && cached2.cliName === this.cliName) {
41534
- return cached2.result;
41910
+ if (cached22 && cached22.responseBuffer === this.responseBuffer && cached22.currentTurnScope === this.engine.currentTurnScope && cached22.recentOutputBuffer === this.recentOutputBuffer && cached22.accumulatedBuffer === this.accumulatedBuffer && cached22.accumulatedRawBufferKey === accumulatedRawBufferKey && cached22.screenText === this.lastScreenText && cached22.currentStatus === this.engine.currentStatus && cached22.activeModal === this.engine.activeModal && cached22.cliName === this.cliName) {
41911
+ return cached22.result;
41535
41912
  }
41536
41913
  return null;
41537
41914
  }
@@ -41991,10 +42368,10 @@ ${lastSnapshot}`;
41991
42368
  getScriptParsedStatus() {
41992
42369
  const screenText = this.readTerminalScreenText();
41993
42370
  const parseScreenText = this.getParseScreenText(screenText);
41994
- const cached2 = this.parsedStatusCache;
42371
+ const cached22 = this.parsedStatusCache;
41995
42372
  const accumulatedRawBufferKey = this.getAccumulatedRawBufferCacheKey();
41996
- if (!this.providerOwnsTranscript() && cached2 && cached2.responseBuffer === this.responseBuffer && cached2.currentTurnScope === this.engine.currentTurnScope && cached2.recentOutputBuffer === this.recentOutputBuffer && cached2.accumulatedBuffer === this.accumulatedBuffer && cached2.accumulatedRawBufferKey === accumulatedRawBufferKey && cached2.screenText === parseScreenText && cached2.currentStatus === this.engine.currentStatus && cached2.activeModal === this.engine.activeModal && cached2.cliName === this.cliName) {
41997
- return cached2.result;
42373
+ if (!this.providerOwnsTranscript() && cached22 && cached22.responseBuffer === this.responseBuffer && cached22.currentTurnScope === this.engine.currentTurnScope && cached22.recentOutputBuffer === this.recentOutputBuffer && cached22.accumulatedBuffer === this.accumulatedBuffer && cached22.accumulatedRawBufferKey === accumulatedRawBufferKey && cached22.screenText === parseScreenText && cached22.currentStatus === this.engine.currentStatus && cached22.activeModal === this.engine.activeModal && cached22.cliName === this.cliName) {
42374
+ return cached22.result;
41998
42375
  }
41999
42376
  const parsed = this.runParseSession();
42000
42377
  if (!parsed || !Array.isArray(parsed.messages)) {
@@ -43865,6 +44242,7 @@ ${lastSnapshot}`;
43865
44242
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
43866
44243
  NodePtyTransportFactory: () => NodePtyTransportFactory,
43867
44244
  P2pRelayFailureError: () => P2pRelayFailureError,
44245
+ PRUNABLE_ORPHAN_STALE_REASONS: () => PRUNABLE_ORPHAN_STALE_REASONS,
43868
44246
  ProviderCliAdapter: () => ProviderCliAdapter,
43869
44247
  ProviderInstanceManager: () => ProviderInstanceManager,
43870
44248
  ProviderLoader: () => ProviderLoader,
@@ -43918,6 +44296,7 @@ ${lastSnapshot}`;
43918
44296
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
43919
44297
  classifyHotChatSessionsForSubscriptionFlush: () => classifyHotChatSessionsForSubscriptionFlush2,
43920
44298
  classifyP2pRelayFailure: () => classifyP2pRelayFailure,
44299
+ classifyStaleDirectForPrune: () => classifyStaleDirectForPrune,
43921
44300
  cleanupTerminalDirectDispatches: () => cleanupTerminalDirectDispatches,
43922
44301
  clearDebugTrace: () => clearDebugTrace,
43923
44302
  clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
@@ -43937,6 +44316,7 @@ ${lastSnapshot}`;
43937
44316
  createNativeHistoryDispatcher: () => createNativeHistoryDispatcher,
43938
44317
  createSessionDelivery: () => createSessionDelivery,
43939
44318
  createWorktree: () => createWorktree,
44319
+ deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
43940
44320
  deleteMesh: () => deleteMesh,
43941
44321
  deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems,
43942
44322
  describeTaskDependencyState: () => describeTaskDependencyState,
@@ -43965,6 +44345,7 @@ ${lastSnapshot}`;
43965
44345
  getAvailableIdeIds: () => getAvailableIdeIds,
43966
44346
  getCoordinatorForSession: () => getCoordinatorForSession,
43967
44347
  getCurrentDaemonLogPath: () => getCurrentDaemonLogPath,
44348
+ getDaemonBuildInfo: () => getDaemonBuildInfo,
43968
44349
  getDaemonDataDir: () => getDaemonDataDir,
43969
44350
  getDaemonLogDir: () => getDaemonLogDir,
43970
44351
  getDebugRuntimeConfig: () => getDebugRuntimeConfig,
@@ -46761,6 +47142,17 @@ ${lastSnapshot}`;
46761
47142
  }
46762
47143
  return { activeWork: records, staleDirectWork, staleDirectWorkNote, terminalDirectWork, summary };
46763
47144
  }
47145
+ var PRUNABLE_ORPHAN_STALE_REASONS = /* @__PURE__ */ new Set([
47146
+ "direct task node is no longer in the live mesh",
47147
+ "direct task session is not present in live session records",
47148
+ "direct task has no node id"
47149
+ ]);
47150
+ function classifyStaleDirectForPrune(record2, opts = {}) {
47151
+ if (record2.staleDispatchUnacknowledged === true) return "preserve_unacknowledged";
47152
+ if (record2.terminal === true) return opts.includeTerminal ? "prunable_terminal" : "preserve_active";
47153
+ if (record2.staleReason && PRUNABLE_ORPHAN_STALE_REASONS.has(record2.staleReason)) return "prunable_orphan";
47154
+ return "preserve_active";
47155
+ }
46764
47156
  function buildCompactStaleDirectWorkSummary(staleDirectWork, opts = {}) {
46765
47157
  const sampleLimit = Math.max(0, Math.min(10, Math.floor(opts.sampleLimit ?? 3)));
46766
47158
  const reasonCounts = {};
@@ -49950,9 +50342,9 @@ ${cleanBody}`;
49950
50342
  for (const file2 of files.slice().sort()) {
49951
50343
  const filePath = path12.join(dir, file2);
49952
50344
  const signature = fileSignatures.get(file2) || `${file2}:missing`;
49953
- const cached2 = savedHistoryFileSummaryCache.get(filePath);
50345
+ const cached22 = savedHistoryFileSummaryCache.get(filePath);
49954
50346
  const persisted = persistedEntries.get(file2);
49955
- const reusableEntry = cached2?.signature === signature ? cached2 : persisted?.signature === signature ? persisted : null;
50347
+ const reusableEntry = cached22?.signature === signature ? cached22 : persisted?.signature === signature ? persisted : null;
49956
50348
  const fileSummary = reusableEntry?.summary || computeSavedHistoryFileSummary(dir, file2);
49957
50349
  const nextEntry = reusableEntry || {
49958
50350
  signature,
@@ -50404,23 +50796,23 @@ ${cleanBody}`;
50404
50796
  savedHistorySessionCache.delete(sanitized);
50405
50797
  return { sessions: [], hasMore: false };
50406
50798
  }
50407
- const cached2 = savedHistorySessionCache.get(sanitized);
50799
+ const cached22 = savedHistorySessionCache.get(sanitized);
50408
50800
  const offset = Math.max(0, options.offset || 0);
50409
50801
  const limit = Math.max(1, options.limit || 30);
50410
50802
  const indexSignature = buildSavedHistoryIndexFileSignature(dir);
50411
50803
  let cacheWasInvalidated = false;
50412
- if (cached2) {
50413
- const cacheLooksPersisted = cached2.signature.startsWith("index:");
50414
- const cacheStillValid = cacheLooksPersisted ? cached2.signature === indexSignature : (() => {
50804
+ if (cached22) {
50805
+ const cacheLooksPersisted = cached22.signature.startsWith("index:");
50806
+ const cacheStillValid = cacheLooksPersisted ? cached22.signature === indexSignature : (() => {
50415
50807
  const files2 = listHistoryFiles(dir);
50416
50808
  const fileSignatures2 = buildSavedHistoryFileSignatureMap(dir, files2);
50417
- return cached2.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
50809
+ return cached22.signature === buildSavedHistoryCacheSignature(files2, fileSignatures2);
50418
50810
  })();
50419
50811
  if (cacheStillValid) {
50420
- const sliced2 = cached2.summaries.slice(offset, offset + limit);
50812
+ const sliced2 = cached22.summaries.slice(offset, offset + limit);
50421
50813
  return {
50422
50814
  sessions: sliced2,
50423
- hasMore: cached2.summaries.length > offset + limit
50815
+ hasMore: cached22.summaries.length > offset + limit
50424
50816
  };
50425
50817
  }
50426
50818
  cacheWasInvalidated = true;
@@ -66889,8 +67281,8 @@ Run 'adhdev doctor' for detailed diagnostics.`
66889
67281
  return null;
66890
67282
  }
66891
67283
  registerProviderScriptRootSafely(path30.dirname(path30.dirname(providerDir)));
66892
- const cached2 = this.scriptsCache.get(dir);
66893
- if (cached2) return cached2;
67284
+ const cached22 = this.scriptsCache.get(dir);
67285
+ if (cached22) return cached22;
66894
67286
  const scriptsJs = path30.join(dir, "scripts.js");
66895
67287
  if (fs20.existsSync(scriptsJs)) {
66896
67288
  try {
@@ -68869,6 +69261,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
68869
69261
  }
68870
69262
  };
68871
69263
  }
69264
+ init_build_info();
68872
69265
  var import_child_process7 = require("child_process");
68873
69266
  var import_child_process8 = require("child_process");
68874
69267
  var fs222 = __toESM2(require("fs"));
@@ -69651,13 +70044,13 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
69651
70044
  nodes
69652
70045
  };
69653
70046
  }
69654
- function reconcileInlineMeshCache(cached2, incoming) {
69655
- if (!cached2 || typeof cached2 !== "object" || Array.isArray(cached2)) return incoming;
69656
- if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached2;
69657
- const cachedNodes = Array.isArray(cached2.nodes) ? cached2.nodes : [];
70047
+ function reconcileInlineMeshCache(cached22, incoming) {
70048
+ if (!cached22 || typeof cached22 !== "object" || Array.isArray(cached22)) return incoming;
70049
+ if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached22;
70050
+ const cachedNodes = Array.isArray(cached22.nodes) ? cached22.nodes : [];
69658
70051
  const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
69659
- if (!cachedNodes.length || !incomingNodes.length) return { ...cached2, ...incoming };
69660
- const cachedUpdatedAt = Date.parse(readStringValue(cached2.updatedAt, cached2.updated_at) || "");
70052
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached22, ...incoming };
70053
+ const cachedUpdatedAt = Date.parse(readStringValue(cached22.updatedAt, cached22.updated_at) || "");
69661
70054
  const incomingUpdatedAt = Date.parse(readStringValue(incoming.updatedAt, incoming.updated_at) || "");
69662
70055
  const preserveCachedMembership = Number.isFinite(cachedUpdatedAt) && (!Number.isFinite(incomingUpdatedAt) || cachedUpdatedAt > incomingUpdatedAt);
69663
70056
  const cachedById = /* @__PURE__ */ new Map();
@@ -69686,7 +70079,7 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
69686
70079
  }
69687
70080
  }
69688
70081
  return {
69689
- ...cached2,
70082
+ ...cached22,
69690
70083
  ...incoming,
69691
70084
  nodes
69692
70085
  };
@@ -70297,8 +70690,37 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
70297
70690
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
70298
70691
  });
70299
70692
  const mergeBase = git(["merge-base", baseHead, branchHead]).trim();
70300
- const mergeTreeStdout = git(["merge-tree", "--write-tree", baseHead, branchHead]);
70301
- const mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || "";
70693
+ let mergedTree = "";
70694
+ let mergeTreeStdout = "";
70695
+ let gitlinkTrivialFastForward;
70696
+ try {
70697
+ mergeTreeStdout = git(["merge-tree", "--write-tree", baseHead, branchHead]);
70698
+ mergedTree = mergeTreeStdout.trim().split(/\s+/)[0] || "";
70699
+ } catch (mergeTreeErr) {
70700
+ const output = `${mergeTreeErr?.message || ""}
70701
+ ${mergeTreeErr?.stdout || ""}
70702
+ ${mergeTreeErr?.stderr || ""}`;
70703
+ const isSubmoduleConflict = /(submodule|160000)/i.test(output) || /Recursive merging with submodules/i.test(output);
70704
+ if (!isSubmoduleConflict) throw mergeTreeErr;
70705
+ const evaluation = evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead);
70706
+ if (!evaluation.trivial) {
70707
+ return {
70708
+ status: "failed",
70709
+ equivalent: false,
70710
+ baseHead,
70711
+ branchHead,
70712
+ mergeBase: mergeBase || void 0,
70713
+ durationMs: Date.now() - startedAt,
70714
+ error: mergeTreeErr?.message || String(mergeTreeErr),
70715
+ stdout: truncateValidationOutput(mergeTreeErr?.stdout),
70716
+ stderr: truncateValidationOutput(mergeTreeErr?.stderr),
70717
+ gitlinkTrivialFastForward: { resolved: false, gitlinks: evaluation.gitlinks, reason: evaluation.reason },
70718
+ actionableHint: buildPatchEquivalenceSubmoduleConflictHint(repoRoot, baseHead, branchHead, output)
70719
+ };
70720
+ }
70721
+ mergedTree = synthesizeTrivialFastForwardMergeTree(repoRoot, baseHead, branchHead, evaluation.gitlinks) || "";
70722
+ gitlinkTrivialFastForward = { resolved: true, gitlinks: evaluation.gitlinks };
70723
+ }
70302
70724
  if (!mergeBase || !mergedTree) {
70303
70725
  return {
70304
70726
  status: "failed",
@@ -70309,7 +70731,8 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
70309
70731
  mergedTree: mergedTree || void 0,
70310
70732
  durationMs: Date.now() - startedAt,
70311
70733
  error: "patch equivalence preflight could not resolve merge-base or synthetic merge tree",
70312
- stdout: truncateValidationOutput(mergeTreeStdout)
70734
+ stdout: truncateValidationOutput(mergeTreeStdout),
70735
+ gitlinkTrivialFastForward
70313
70736
  };
70314
70737
  }
70315
70738
  const expectedPatchId = await computeGitPatchId(repoRoot, mergeBase, branchHead);
@@ -70324,7 +70747,8 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
70324
70747
  mergedTree,
70325
70748
  expectedPatchId,
70326
70749
  actualPatchId,
70327
- durationMs: Date.now() - startedAt
70750
+ durationMs: Date.now() - startedAt,
70751
+ gitlinkTrivialFastForward
70328
70752
  };
70329
70753
  } catch (e) {
70330
70754
  return {
@@ -70347,6 +70771,65 @@ ${e?.stderr || ""}`
70347
70771
  };
70348
70772
  }
70349
70773
  }
70774
+ async function runMeshRefineEffectiveDiffGate(repoRoot, baseHead, branchHead) {
70775
+ const startedAt = Date.now();
70776
+ try {
70777
+ const { execFileSync: execFileSync6 } = await import("child_process");
70778
+ const git = (args, opts) => execFileSync6("git", args, {
70779
+ cwd: opts?.cwd || repoRoot,
70780
+ encoding: "utf8",
70781
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
70782
+ });
70783
+ const rawDiff = git(["diff", "--raw", baseHead, branchHead]).trim();
70784
+ if (rawDiff) {
70785
+ const changedPaths = rawDiff.split("\n").map((line) => line.split(" ").slice(1).join(" ").trim()).filter(Boolean).slice(0, 50);
70786
+ return {
70787
+ status: "passed",
70788
+ hasEffectiveDiff: true,
70789
+ baseHead,
70790
+ branchHead,
70791
+ changedPaths,
70792
+ durationMs: Date.now() - startedAt
70793
+ };
70794
+ }
70795
+ const submoduleHints = [];
70796
+ try {
70797
+ const status = git(["submodule", "status"]);
70798
+ for (const line of status.split("\n")) {
70799
+ const trimmed = line.trimEnd();
70800
+ if (!trimmed) continue;
70801
+ if (trimmed.startsWith("+")) {
70802
+ const parts = trimmed.slice(1).trim().split(/\s+/);
70803
+ const path39 = parts[1] || parts[0] || "(unknown)";
70804
+ submoduleHints.push({
70805
+ path: path39,
70806
+ reason: "submodule checked-out commit differs from the committed gitlink (pointer bump not committed on the root branch)"
70807
+ });
70808
+ }
70809
+ }
70810
+ } catch {
70811
+ }
70812
+ return {
70813
+ status: "failed",
70814
+ hasEffectiveDiff: false,
70815
+ baseHead,
70816
+ branchHead,
70817
+ ...submoduleHints.length ? { submoduleHints } : {},
70818
+ durationMs: Date.now() - startedAt
70819
+ };
70820
+ } catch (e) {
70821
+ return {
70822
+ status: "skipped",
70823
+ hasEffectiveDiff: true,
70824
+ baseHead,
70825
+ branchHead,
70826
+ durationMs: Date.now() - startedAt,
70827
+ error: e?.message || String(e),
70828
+ stdout: truncateValidationOutput(e?.stdout),
70829
+ stderr: truncateValidationOutput(e?.stderr)
70830
+ };
70831
+ }
70832
+ }
70350
70833
  function buildPatchEquivalenceSubmoduleConflictHint(repoRoot, baseHead, branchHead, output) {
70351
70834
  if (!/(submodule|160000)/i.test(output) || !/(conflict|failed to merge)/i.test(output)) return void 0;
70352
70835
  const conflicts = readChangedGitlinkPaths(repoRoot, baseHead, branchHead).map((path39) => ({
@@ -70403,6 +70886,135 @@ ${e?.stderr || ""}`
70403
70886
  return void 0;
70404
70887
  }
70405
70888
  }
70889
+ function resolveGitDir(repoRoot) {
70890
+ const out = (0, import_node_child_process6.execFileSync)("git", ["rev-parse", "--absolute-git-dir"], {
70891
+ cwd: repoRoot,
70892
+ encoding: "utf8",
70893
+ maxBuffer: 1024 * 1024
70894
+ }).trim();
70895
+ return out;
70896
+ }
70897
+ function isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
70898
+ if (!baseCommit || !branchCommit) return false;
70899
+ if (baseCommit === branchCommit) return true;
70900
+ try {
70901
+ if (!fs23.existsSync(submoduleRepoPath)) return false;
70902
+ (0, import_node_child_process6.execFileSync)("git", ["cat-file", "-e", `${baseCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
70903
+ (0, import_node_child_process6.execFileSync)("git", ["cat-file", "-e", `${branchCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
70904
+ (0, import_node_child_process6.execFileSync)("git", ["merge-base", "--is-ancestor", baseCommit, branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
70905
+ return true;
70906
+ } catch {
70907
+ return false;
70908
+ }
70909
+ }
70910
+ function readChangedPathKinds(repoRoot, fromRef, toRef) {
70911
+ try {
70912
+ const output = (0, import_node_child_process6.execFileSync)("git", ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
70913
+ cwd: repoRoot,
70914
+ encoding: "utf8",
70915
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
70916
+ });
70917
+ const result = [];
70918
+ const seen = /* @__PURE__ */ new Set();
70919
+ for (const line of output.split("\n")) {
70920
+ if (!line.trim()) continue;
70921
+ const metaAndPath = line.split(" ");
70922
+ const meta3 = metaAndPath[0] || "";
70923
+ const path39 = metaAndPath[metaAndPath.length - 1]?.trim();
70924
+ if (!path39 || seen.has(path39)) continue;
70925
+ seen.add(path39);
70926
+ const parts = meta3.split(/\s+/);
70927
+ const isGitlink = !!(parts[0]?.includes("160000") || parts[1]?.includes("160000"));
70928
+ result.push({ path: path39, isGitlink });
70929
+ }
70930
+ return result;
70931
+ } catch {
70932
+ return [];
70933
+ }
70934
+ }
70935
+ function evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead) {
70936
+ const changedGitlinks = readChangedGitlinkPaths(repoRoot, baseHead, branchHead).map((path39) => {
70937
+ const baseCommit = readTreeObject(repoRoot, baseHead, path39);
70938
+ const branchCommit = readTreeObject(repoRoot, branchHead, path39);
70939
+ const submoduleRepoPath = (0, import_path10.resolve)(repoRoot, path39);
70940
+ const fastForward = !!baseCommit && !!branchCommit && isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit);
70941
+ return { path: path39, baseCommit, branchCommit, fastForward };
70942
+ });
70943
+ if (changedGitlinks.length === 0) {
70944
+ return { trivial: false, reason: "no_changed_gitlinks", gitlinks: changedGitlinks };
70945
+ }
70946
+ const nonFastForward = changedGitlinks.filter((entry) => !entry.fastForward);
70947
+ if (nonFastForward.length > 0) {
70948
+ return {
70949
+ trivial: false,
70950
+ reason: `diverged_gitlinks:${nonFastForward.map((entry) => entry.path).join(",")}`,
70951
+ gitlinks: changedGitlinks
70952
+ };
70953
+ }
70954
+ let mergeBase = "";
70955
+ try {
70956
+ mergeBase = (0, import_node_child_process6.execFileSync)("git", ["merge-base", baseHead, branchHead], {
70957
+ cwd: repoRoot,
70958
+ encoding: "utf8",
70959
+ maxBuffer: 1024 * 1024
70960
+ }).trim();
70961
+ } catch {
70962
+ return { trivial: false, reason: "merge_base_unresolved", gitlinks: changedGitlinks };
70963
+ }
70964
+ if (!mergeBase) {
70965
+ return { trivial: false, reason: "merge_base_unresolved", gitlinks: changedGitlinks };
70966
+ }
70967
+ const baseSideChanges = readChangedPathKinds(repoRoot, mergeBase, baseHead);
70968
+ const branchSideChanges = readChangedPathKinds(repoRoot, mergeBase, branchHead);
70969
+ const baseChangedPaths = new Map(baseSideChanges.map((entry) => [entry.path, entry]));
70970
+ const overlapping = branchSideChanges.filter((entry) => baseChangedPaths.has(entry.path));
70971
+ const nonGitlinkOverlap = overlapping.filter((entry) => {
70972
+ const baseEntry = baseChangedPaths.get(entry.path);
70973
+ return !(entry.isGitlink && baseEntry?.isGitlink);
70974
+ });
70975
+ if (nonGitlinkOverlap.length > 0) {
70976
+ return {
70977
+ trivial: false,
70978
+ reason: `non_gitlink_overlap:${nonGitlinkOverlap.map((entry) => entry.path).join(",")}`,
70979
+ gitlinks: changedGitlinks
70980
+ };
70981
+ }
70982
+ return { trivial: true, gitlinks: changedGitlinks };
70983
+ }
70984
+ function synthesizeTrivialFastForwardMergeTree(repoRoot, baseHead, branchHead, gitlinks) {
70985
+ try {
70986
+ const baseTree = (0, import_node_child_process6.execFileSync)("git", ["rev-parse", `${baseHead}^{tree}`], {
70987
+ cwd: repoRoot,
70988
+ encoding: "utf8",
70989
+ maxBuffer: 1024 * 1024
70990
+ }).trim();
70991
+ if (!baseTree) return void 0;
70992
+ const updates = gitlinks.filter((entry) => entry.branchCommit).map((entry) => `160000 commit ${entry.branchCommit} ${entry.path}`).join("\n");
70993
+ if (!updates) return baseTree;
70994
+ const tmpIndex = (0, import_path10.join)(resolveGitDir(repoRoot), `adhdev-refine-ff-${baseHead.slice(0, 12)}-${branchHead.slice(0, 12)}.index`);
70995
+ const env2 = { ...process.env, GIT_INDEX_FILE: tmpIndex };
70996
+ try {
70997
+ (0, import_node_child_process6.execFileSync)("git", ["read-tree", baseTree], { cwd: repoRoot, env: env2, stdio: "ignore" });
70998
+ (0, import_node_child_process6.execFileSync)("git", ["update-index", "--index-info"], {
70999
+ cwd: repoRoot,
71000
+ env: env2,
71001
+ input: `${updates}
71002
+ `,
71003
+ encoding: "utf8",
71004
+ stdio: ["pipe", "ignore", "ignore"]
71005
+ });
71006
+ const newTree = (0, import_node_child_process6.execFileSync)("git", ["write-tree"], { cwd: repoRoot, env: env2, encoding: "utf8" }).trim();
71007
+ return newTree || void 0;
71008
+ } finally {
71009
+ try {
71010
+ fs23.rmSync(tmpIndex, { force: true });
71011
+ } catch {
71012
+ }
71013
+ }
71014
+ } catch {
71015
+ return void 0;
71016
+ }
71017
+ }
70406
71018
  async function alignRefinerySubmodulesAfterMerge(repoRoot, previousBaseHead, currentHead, options = {}) {
70407
71019
  const startedAt = Date.now();
70408
71020
  const changedGitlinkPaths = readChangedGitlinkPaths(repoRoot, previousBaseHead, currentHead).filter((path39) => !(options.submoduleIgnorePaths || []).includes(path39));
@@ -71070,6 +71682,10 @@ ${e?.stderr || ""}`
71070
71682
  runningRefineJobs = /* @__PURE__ */ new Map();
71071
71683
  /** Terminal async Refinery jobs preserve a clear answer after the worktree node has been removed. */
71072
71684
  terminalRefineJobs = /* @__PURE__ */ new Map();
71685
+ /** In-memory async batch Refinery jobs keyed by meshId (one batch convergence per mesh at a time). */
71686
+ runningRefineBatchJobs = /* @__PURE__ */ new Map();
71687
+ /** Terminal async batch Refinery jobs preserve the last batch outcome for late readers. */
71688
+ terminalRefineBatchJobs = /* @__PURE__ */ new Map();
71073
71689
  constructor(deps) {
71074
71690
  this.deps = deps;
71075
71691
  }
@@ -71147,13 +71763,13 @@ ${e?.stderr || ""}`
71147
71763
  };
71148
71764
  }
71149
71765
  getCachedAggregateMeshStatus(meshId, mesh, options) {
71150
- const cached2 = this.aggregateMeshStatusCache.get(meshId);
71151
- if (!cached2?.snapshot || cached2.snapshot.success !== true || !Array.isArray(cached2.snapshot.nodes)) return null;
71152
- if (cached2.queueRevision !== getMeshQueueRevision(meshId)) return null;
71153
- let snapshot = this.cloneJsonValue(cached2.snapshot);
71766
+ const cached22 = this.aggregateMeshStatusCache.get(meshId);
71767
+ if (!cached22?.snapshot || cached22.snapshot.success !== true || !Array.isArray(cached22.snapshot.nodes)) return null;
71768
+ if (cached22.queueRevision !== getMeshQueueRevision(meshId)) return null;
71769
+ let snapshot = this.cloneJsonValue(cached22.snapshot);
71154
71770
  snapshot = this.hydrateCachedAggregateMeshStatusFromInline(snapshot, mesh, options);
71155
71771
  if (shouldRefreshStalePendingAggregate(snapshot, options)) return null;
71156
- const ageMs = Math.max(0, Date.now() - cached2.builtAt);
71772
+ const ageMs = Math.max(0, Date.now() - cached22.builtAt);
71157
71773
  const sourceOfTruth = snapshot.sourceOfTruth && typeof snapshot.sourceOfTruth === "object" ? snapshot.sourceOfTruth : {};
71158
71774
  snapshot.sourceOfTruth = {
71159
71775
  ...sourceOfTruth,
@@ -71164,7 +71780,7 @@ ${e?.stderr || ""}`
71164
71780
  source: "memory",
71165
71781
  refreshReason: "memory_cache_hit",
71166
71782
  ageMs,
71167
- cachedAt: new Date(cached2.builtAt).toISOString(),
71783
+ cachedAt: new Date(cached22.builtAt).toISOString(),
71168
71784
  returnedAt: (/* @__PURE__ */ new Date()).toISOString()
71169
71785
  }
71170
71786
  };
@@ -71208,9 +71824,9 @@ ${e?.stderr || ""}`
71208
71824
  warmInlineMeshCache(meshId, inlineMesh) {
71209
71825
  if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
71210
71826
  const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
71211
- const cached2 = this.inlineMeshCache.get(meshId);
71212
- if (cached2) {
71213
- const merged = reconcileInlineMeshCache(cached2, sanitizedInlineMesh);
71827
+ const cached22 = this.inlineMeshCache.get(meshId);
71828
+ if (cached22) {
71829
+ const merged = reconcileInlineMeshCache(cached22, sanitizedInlineMesh);
71214
71830
  this.inlineMeshCache.set(meshId, merged);
71215
71831
  return merged;
71216
71832
  }
@@ -71220,14 +71836,14 @@ ${e?.stderr || ""}`
71220
71836
  async getMeshForCommand(meshId, inlineMesh, options) {
71221
71837
  const preferInline = options?.preferInline === true;
71222
71838
  if (preferInline) {
71223
- const cached22 = this.getCachedInlineMesh(meshId);
71224
- if (cached22) {
71839
+ const cached3 = this.getCachedInlineMesh(meshId);
71840
+ if (cached3) {
71225
71841
  if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
71226
- const merged = reconcileInlineMeshCache(cached22, inlineMesh);
71842
+ const merged = reconcileInlineMeshCache(cached3, inlineMesh);
71227
71843
  this.inlineMeshCache.set(meshId, sanitizeInlineMesh(merged));
71228
71844
  return { mesh: merged, inline: true, source: "inline_cache" };
71229
71845
  }
71230
- return { mesh: cached22, inline: true, source: "inline_cache" };
71846
+ return { mesh: cached3, inline: true, source: "inline_cache" };
71231
71847
  }
71232
71848
  if (inlineMeshCarriesTransientNodeTruth(inlineMesh)) {
71233
71849
  this.warmInlineMeshCache(meshId, inlineMesh);
@@ -71240,8 +71856,8 @@ ${e?.stderr || ""}`
71240
71856
  if (mesh) return { mesh, inline: false, source: "local_config" };
71241
71857
  } catch {
71242
71858
  }
71243
- const cached2 = this.getCachedInlineMesh(meshId);
71244
- if (cached2) return { mesh: cached2, inline: true, source: "inline_cache" };
71859
+ const cached22 = this.getCachedInlineMesh(meshId);
71860
+ if (cached22) return { mesh: cached22, inline: true, source: "inline_cache" };
71245
71861
  const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
71246
71862
  return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
71247
71863
  }
@@ -71548,6 +72164,8 @@ ${e?.stderr || ""}`
71548
72164
  const skippedSessionIds = [];
71549
72165
  const skippedLiveSessionIds = [];
71550
72166
  const skippedCoordinatorSessionIds = [];
72167
+ const skippedLiveSessionReasons = [];
72168
+ const actedLiveDelegateSessionIds = [];
71551
72169
  const deleteUnsupportedSessionIds = [];
71552
72170
  const recordsRemainSessionIds = [];
71553
72171
  const errors = [];
@@ -71581,16 +72199,31 @@ ${e?.stderr || ""}`
71581
72199
  const surfaceKind = getSessionHostSurfaceKind(record2);
71582
72200
  const liveRuntime = surfaceKind === "live_runtime";
71583
72201
  const coordinatorSession = readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId;
72202
+ const recordNodeId = readStringValue(record2?.meta?.meshNodeId);
72203
+ const recordMeshNodeFor = readStringValue(record2?.meta?.meshNodeFor);
72204
+ const delegateBoundToThisNode = !!recordNodeId && recordNodeId === args.nodeId && (!recordMeshNodeFor || recordMeshNodeFor === args.meshId);
71584
72205
  if (!hasExplicitSessionIds && coordinatorSession) {
71585
72206
  skippedSessionIds.push(sessionId);
71586
72207
  skippedCoordinatorSessionIds.push(sessionId);
71587
72208
  continue;
71588
72209
  }
71589
- if (!hasExplicitSessionIds && liveRuntime) {
72210
+ if (!hasExplicitSessionIds && liveRuntime && !delegateBoundToThisNode) {
71590
72211
  skippedSessionIds.push(sessionId);
71591
72212
  skippedLiveSessionIds.push(sessionId);
72213
+ const matchedByWorkspaceOnly = !recordNodeId;
72214
+ const reason = recordNodeId && recordNodeId !== args.nodeId ? `live_delegate_bound_to_other_node:${recordNodeId}` : matchedByWorkspaceOnly ? "live_session_matched_by_workspace_only_no_node_binding" : "live_session_not_bound_to_this_node";
72215
+ skippedLiveSessionReasons.push({ sessionId, reason });
71592
72216
  continue;
71593
72217
  }
72218
+ if (!hasExplicitSessionIds && liveRuntime && delegateBoundToThisNode && args.mode === "delete_stopped") {
72219
+ skippedSessionIds.push(sessionId);
72220
+ skippedLiveSessionIds.push(sessionId);
72221
+ skippedLiveSessionReasons.push({ sessionId, reason: "live_delegate_preserved_by_delete_stopped_mode_use_stop_or_stop_and_delete" });
72222
+ continue;
72223
+ }
72224
+ if (!hasExplicitSessionIds && liveRuntime && delegateBoundToThisNode) {
72225
+ actedLiveDelegateSessionIds.push(sessionId);
72226
+ }
71594
72227
  try {
71595
72228
  if (args.mode === "stop") {
71596
72229
  if (!completed) {
@@ -71652,6 +72285,8 @@ ${e?.stderr || ""}`
71652
72285
  skippedSessionIds,
71653
72286
  skippedLiveSessionIds,
71654
72287
  skippedCoordinatorSessionIds,
72288
+ ...actedLiveDelegateSessionIds.length ? { actedLiveDelegateSessionIds } : {},
72289
+ ...skippedLiveSessionReasons.length ? { skippedLiveSessionReasons } : {},
71655
72290
  ...deleteUnsupported ? {
71656
72291
  deleteUnsupported: true,
71657
72292
  effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
@@ -72299,6 +72934,48 @@ ${tail}` : ""
72299
72934
  }
72300
72935
  };
72301
72936
  }
72937
+ const effectiveDiffStarted = Date.now();
72938
+ const effectiveDiff = await runMeshRefineEffectiveDiffGate(repoRoot, baseHead, branchHead);
72939
+ recordMeshRefineStage(refineStages, "effective_diff", effectiveDiff.status, effectiveDiffStarted, {
72940
+ hasEffectiveDiff: effectiveDiff.hasEffectiveDiff,
72941
+ changedPaths: effectiveDiff.changedPaths,
72942
+ submoduleHints: effectiveDiff.submoduleHints,
72943
+ ...effectiveDiff.error ? { error: effectiveDiff.error } : {}
72944
+ });
72945
+ if (effectiveDiff.status === "failed" && !effectiveDiff.hasEffectiveDiff) {
72946
+ const hintLines = (effectiveDiff.submoduleHints || []).map((h) => ` - ${h.path}: ${h.reason}`);
72947
+ const message = [
72948
+ `Refinery no-op guard: branch '${branch}' has no effective root-tree diff against '${baseBranch}' (${baseHead.slice(0, 12)}); nothing would merge.`,
72949
+ "This usually means a submodule (e.g. oss) has commits but the root branch never committed the gitlink (pointer) bump, so the merge would be a silent no-op while the real change never reaches main.",
72950
+ hintLines.length ? `Submodules with uncommitted pointer bumps:
72951
+ ${hintLines.join("\n")}` : "",
72952
+ `Fix: commit the submodule pointer bump on '${branch}' (git add <submodule-path> && git commit), then re-run refine.`
72953
+ ].filter(Boolean).join("\n");
72954
+ return {
72955
+ success: false,
72956
+ code: "no_effective_diff",
72957
+ convergenceStatus: "blocked_review",
72958
+ error: message,
72959
+ branch,
72960
+ into: baseBranch,
72961
+ validationSummary,
72962
+ patchEquivalence,
72963
+ effectiveDiff,
72964
+ refineStages,
72965
+ finalBranchConvergenceState: {
72966
+ branch,
72967
+ baseBranch,
72968
+ merged: false,
72969
+ removed: false,
72970
+ validation: "passed",
72971
+ patchEquivalence: "passed",
72972
+ effectiveDiff: "no_effective_diff",
72973
+ status: "blocked_review",
72974
+ reason: "no_effective_diff",
72975
+ ...effectiveDiff.submoduleHints?.length ? { submoduleHints: effectiveDiff.submoduleHints } : {}
72976
+ }
72977
+ };
72978
+ }
72302
72979
  let mergeResult;
72303
72980
  const mergeStarted = Date.now();
72304
72981
  try {
@@ -72537,8 +73214,8 @@ ${tail}` : ""
72537
73214
  const repoRootBaseRef = /* @__PURE__ */ new Map();
72538
73215
  const submodulePathsByRepoRoot = /* @__PURE__ */ new Map();
72539
73216
  const resolveBaseRef = async (repoRoot) => {
72540
- const cached2 = repoRootBaseRef.get(repoRoot);
72541
- if (cached2) return cached2;
73217
+ const cached22 = repoRootBaseRef.get(repoRoot);
73218
+ if (cached22) return cached22;
72542
73219
  let baseBranch = "main";
72543
73220
  try {
72544
73221
  const { stdout } = await execFileAsync4("git", ["branch", "--show-current"], { cwd: repoRoot, encoding: "utf8" });
@@ -72640,6 +73317,17 @@ ${tail}` : ""
72640
73317
  note: "Dry-run: no validation, rebase, or merge was executed. Re-run with execute=true to converge nodes in this order."
72641
73318
  };
72642
73319
  }
73320
+ return this.runMeshRefineBatchConvergence(meshId, orderedNodes, ordering, args);
73321
+ }
73322
+ /**
73323
+ * Convergence core shared by the synchronous batch entry and the async batch job.
73324
+ * Refines each node in order: the per-node refine pipeline fetches origin/<base>
73325
+ * fresh, so each merged sibling advances the base before the next node's auto-rebase
73326
+ * + patch-equivalence re-check. A blocked/failed node is isolated; the batch
73327
+ * continues with the remaining nodes. Does NOT touch the per-node merge logic — it
73328
+ * only sequences calls to executeMeshRefineNodeSynchronously and aggregates outcomes.
73329
+ */
73330
+ async runMeshRefineBatchConvergence(meshId, orderedNodes, ordering, args) {
72643
73331
  const results = [];
72644
73332
  for (const node of orderedNodes) {
72645
73333
  let result;
@@ -72694,6 +73382,204 @@ ${tail}` : ""
72694
73382
  }
72695
73383
  };
72696
73384
  }
73385
+ buildRefineBatchJobKey(meshId) {
73386
+ return `${meshId}::batch`;
73387
+ }
73388
+ buildRefineBatchJobHandle(args) {
73389
+ return {
73390
+ success: true,
73391
+ async: true,
73392
+ batch: true,
73393
+ status: args.status || "accepted",
73394
+ jobId: args.jobId || `refine_batch_${createInteractionId()}`,
73395
+ interactionId: args.interactionId || createInteractionId(),
73396
+ meshId: args.meshId,
73397
+ batchLabel: `batch:${args.nodeIds.length} node${args.nodeIds.length === 1 ? "" : "s"}`,
73398
+ nodeIds: args.nodeIds,
73399
+ nodeCount: args.nodeIds.length,
73400
+ order: args.order,
73401
+ startedAt: args.startedAt || (/* @__PURE__ */ new Date()).toISOString(),
73402
+ ...args.completedAt ? { completedAt: args.completedAt } : {},
73403
+ ...args.coordinatorDaemonId ? { targetCoordinatorDaemonId: args.coordinatorDaemonId } : {},
73404
+ eventDelivery: { pendingEvents: true, ledger: true },
73405
+ evidence: {
73406
+ pendingEventsCommand: "get_pending_mesh_events",
73407
+ ledgerCommand: "get_mesh_ledger_slice",
73408
+ taskHistoryKind: args.status === "completed" ? "task_completed" : args.status === "failed" ? "task_failed" : "task_dispatched"
73409
+ }
73410
+ };
73411
+ }
73412
+ /**
73413
+ * Emit a batch Refinery terminal/accepted event through the SAME pending-event +
73414
+ * forward mechanism single-node refine uses (queueRefineJobEvent), so the
73415
+ * coordinator's existing refine:accepted/completed/failed handling and message
73416
+ * renderer apply unchanged. The aggregate per-node results ride along in `result`.
73417
+ */
73418
+ queueRefineBatchJobEvent(event, handle, result) {
73419
+ const metadataEvent = {
73420
+ source: "refine_mesh_node_async_job",
73421
+ batch: true,
73422
+ jobId: handle.jobId,
73423
+ interactionId: handle.interactionId,
73424
+ meshId: handle.meshId,
73425
+ nodeId: handle.batchLabel,
73426
+ nodeIds: handle.nodeIds,
73427
+ workspace: void 0,
73428
+ status: handle.status,
73429
+ startedAt: handle.startedAt,
73430
+ completedAt: handle.completedAt,
73431
+ order: handle.order,
73432
+ ...result ? { result } : {}
73433
+ };
73434
+ const eventPayload = {
73435
+ event,
73436
+ meshId: handle.meshId,
73437
+ nodeLabel: handle.batchLabel,
73438
+ nodeId: handle.batchLabel,
73439
+ metadataEvent,
73440
+ queuedAt: Date.now(),
73441
+ ...handle.targetCoordinatorDaemonId ? { targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId } : {}
73442
+ };
73443
+ if (typeof this.deps.instanceManager?.getByCategory === "function") {
73444
+ const forwarded = handleMeshForwardEvent(
73445
+ { instanceManager: this.deps.instanceManager },
73446
+ {
73447
+ event,
73448
+ meshId: handle.meshId,
73449
+ nodeId: handle.batchLabel,
73450
+ jobId: handle.jobId,
73451
+ interactionId: handle.interactionId,
73452
+ status: handle.status,
73453
+ startedAt: handle.startedAt,
73454
+ completedAt: handle.completedAt,
73455
+ ...result ? { result } : {}
73456
+ }
73457
+ );
73458
+ if (forwarded?.success === true) return;
73459
+ LOG2.warn("Mesh", `[Refinery] Failed to forward async refine batch event ${event}: ${forwarded?.error || "unknown error"}`);
73460
+ }
73461
+ queuePendingMeshCoordinatorEvent(eventPayload);
73462
+ }
73463
+ async appendRefineBatchJobLedger(kind, handle, result) {
73464
+ try {
73465
+ const { appendLedgerEntry: appendLedgerEntry2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
73466
+ appendLedgerEntry2(handle.meshId, {
73467
+ kind,
73468
+ nodeId: handle.batchLabel,
73469
+ payload: {
73470
+ source: "refine_mesh_node_async_job",
73471
+ refineJob: {
73472
+ batch: true,
73473
+ jobId: handle.jobId,
73474
+ interactionId: handle.interactionId,
73475
+ status: handle.status,
73476
+ meshId: handle.meshId,
73477
+ nodeIds: handle.nodeIds,
73478
+ order: handle.order,
73479
+ targetCoordinatorDaemonId: handle.targetCoordinatorDaemonId,
73480
+ startedAt: handle.startedAt,
73481
+ completedAt: handle.completedAt
73482
+ },
73483
+ async: true,
73484
+ batch: true,
73485
+ ...result ? {
73486
+ success: result.success === true,
73487
+ result
73488
+ } : {}
73489
+ }
73490
+ });
73491
+ } catch (e) {
73492
+ LOG2.warn("Mesh", `[Refinery] Failed to append async refine batch ledger entry: ${e?.message || e}`);
73493
+ }
73494
+ }
73495
+ async finishMeshRefineBatchJob(handle, orderedNodes, ordering, args) {
73496
+ const key = this.buildRefineBatchJobKey(handle.meshId);
73497
+ let result;
73498
+ try {
73499
+ result = await this.runMeshRefineBatchConvergence(handle.meshId, orderedNodes, ordering, args);
73500
+ } catch (e) {
73501
+ result = { success: false, error: e?.message || String(e), batch: true };
73502
+ }
73503
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
73504
+ const summary = result.summary && typeof result.summary === "object" ? result.summary : void 0;
73505
+ const allConverged = result.allConverged === true;
73506
+ const isTerminalSuccess = result.success === true && allConverged;
73507
+ const nextStep = typeof result.nextStep === "string" && result.nextStep ? result.nextStep : isTerminalSuccess ? "All batched nodes converged onto base. Continue from the updated mesh state." : "Resolve blocked_review / not_mergeable nodes (see per-node code/stage/error in result.results), then re-run mesh_refine_batch for the remaining nodes.";
73508
+ const normalizedResult = {
73509
+ ...result,
73510
+ batch: true,
73511
+ nextStep,
73512
+ ...summary ? {
73513
+ convergenceStatus: allConverged ? "all_converged" : "partial"
73514
+ } : {}
73515
+ };
73516
+ const terminalHandle = this.buildRefineBatchJobHandle({
73517
+ meshId: handle.meshId,
73518
+ nodeIds: handle.nodeIds,
73519
+ order: handle.order,
73520
+ status: isTerminalSuccess ? "completed" : "failed",
73521
+ startedAt: handle.startedAt,
73522
+ completedAt,
73523
+ jobId: handle.jobId,
73524
+ interactionId: handle.interactionId,
73525
+ coordinatorDaemonId: handle.targetCoordinatorDaemonId
73526
+ });
73527
+ const terminal = { ...terminalHandle, result: normalizedResult };
73528
+ this.terminalRefineBatchJobs.set(key, terminal);
73529
+ this.runningRefineBatchJobs.delete(key);
73530
+ this.invalidateAggregateMeshStatus(handle.meshId);
73531
+ await this.appendRefineBatchJobLedger(isTerminalSuccess ? "task_completed" : "task_failed", terminalHandle, normalizedResult);
73532
+ this.queueRefineBatchJobEvent(isTerminalSuccess ? "refine:completed" : "refine:failed", terminalHandle, normalizedResult);
73533
+ }
73534
+ /**
73535
+ * Async entry for the batch Refinery execute path. Mirrors startMeshRefineJob:
73536
+ * resolves the plan synchronously (so target/ordering errors and the dry-run shape
73537
+ * stay synchronous), then for execute=true registers an in-flight batch job, returns
73538
+ * {async:true, status:'accepted', batch:true, ...plan} immediately, and runs the
73539
+ * convergence loop in the background — emitting the same terminal refine event.
73540
+ * Idempotent: a batch already in flight for this mesh returns the running handle
73541
+ * with duplicate:true rather than spawning a second background job.
73542
+ */
73543
+ async startMeshRefineBatchJob(meshId, requestedNodeIds, args) {
73544
+ const plan = await this.batchRefineMeshNodes(meshId, requestedNodeIds, { ...args, dryRun: true, execute: false });
73545
+ const planRecord = plan;
73546
+ if (planRecord.success !== true) return plan;
73547
+ if (args?.dryRun === true && args?.execute !== true) return plan;
73548
+ const order = Array.isArray(planRecord.order) ? planRecord.order.filter((v) => typeof v === "string") : [];
73549
+ const nodeIds = order.slice();
73550
+ if (nodeIds.length === 0) {
73551
+ return { ...planRecord, success: true, batch: true, dryRun: false, async: false };
73552
+ }
73553
+ const key = this.buildRefineBatchJobKey(meshId);
73554
+ const running = this.runningRefineBatchJobs.get(key);
73555
+ if (running) return { ...running, duplicate: true };
73556
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
73557
+ const mesh = meshRecord?.mesh;
73558
+ const allNodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
73559
+ const orderedNodes = nodeIds.map((id) => allNodes.find((n) => n.id === id || n.nodeId === id)).filter((n) => !!n);
73560
+ if (orderedNodes.length === 0) {
73561
+ return { success: false, error: "Batch nodes no longer resolvable in mesh", batch: true };
73562
+ }
73563
+ const ordering = {
73564
+ order,
73565
+ rationale: planRecord.orderingRationale
73566
+ };
73567
+ const coordinatorDaemonId = typeof args?.coordinatorDaemonId === "string" && args.coordinatorDaemonId.trim() ? args.coordinatorDaemonId.trim() : this.deps.statusInstanceId || void 0;
73568
+ const handle = this.buildRefineBatchJobHandle({ meshId, nodeIds, order, coordinatorDaemonId });
73569
+ this.runningRefineBatchJobs.set(key, handle);
73570
+ await this.appendRefineBatchJobLedger("task_dispatched", handle);
73571
+ this.queueRefineBatchJobEvent("refine:accepted", handle);
73572
+ setImmediate(() => {
73573
+ void this.finishMeshRefineBatchJob(handle, orderedNodes, ordering, args);
73574
+ });
73575
+ return {
73576
+ ...handle,
73577
+ order,
73578
+ orderingRationale: planRecord.orderingRationale,
73579
+ plan: planRecord.plan,
73580
+ note: "Batch convergence accepted and running in the background. Completion/failure (with per-node results) will be delivered as a terminal refine event; do not poll repeatedly."
73581
+ };
73582
+ }
72697
73583
  async finishMeshRefineJob(handle, args) {
72698
73584
  const key = this.buildRefineJobKey(handle.meshId, handle.targetNodeId);
72699
73585
  let result;
@@ -73227,7 +74113,7 @@ ${tail}` : ""
73227
74113
  version: this.deps.statusVersion || "unknown",
73228
74114
  profile: "metadata"
73229
74115
  });
73230
- return { success: true, status: snapshot };
74116
+ return { success: true, status: snapshot, daemonBuild: getDaemonBuildInfo() };
73231
74117
  }
73232
74118
  case "get_machine_runtime_stats": {
73233
74119
  return {
@@ -74197,6 +75083,7 @@ ${tail}` : ""
74197
75083
  let workspace = typeof args?.workspace === "string" ? args.workspace.trim() : "";
74198
75084
  let submoduleIgnorePaths = Array.isArray(args?.submoduleIgnorePaths) ? args.submoduleIgnorePaths.filter((value) => typeof value === "string") : void 0;
74199
75085
  let nodeDaemonId;
75086
+ let allowAutoPublishSubmoduleMainCommits = false;
74200
75087
  if (meshId && nodeId) {
74201
75088
  const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
74202
75089
  const mesh = meshRecord?.mesh;
@@ -74207,6 +75094,7 @@ ${tail}` : ""
74207
75094
  if (!submoduleIgnorePaths && Array.isArray(node?.policy?.submoduleIgnorePaths)) {
74208
75095
  submoduleIgnorePaths = node.policy.submoduleIgnorePaths.filter((value) => typeof value === "string");
74209
75096
  }
75097
+ allowAutoPublishSubmoduleMainCommits = mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true;
74210
75098
  nodeDaemonId = typeof node?.daemonId === "string" ? node.daemonId.trim() : void 0;
74211
75099
  }
74212
75100
  const selfDaemonId = this.deps.statusInstanceId;
@@ -74227,7 +75115,10 @@ ${tail}` : ""
74227
75115
  execute: args?.execute === true,
74228
75116
  dryRun: args?.dryRun === true,
74229
75117
  updateSubmodules: args?.updateSubmodules === true,
74230
- submoduleIgnorePaths
75118
+ submoduleIgnorePaths,
75119
+ mode: args?.mode === "push" ? "push" : "merge",
75120
+ pushSubmodules: args?.pushSubmodules === true,
75121
+ allowAutoPublishSubmoduleMainCommits
74231
75122
  });
74232
75123
  return result;
74233
75124
  }
@@ -74241,7 +75132,9 @@ ${tail}` : ""
74241
75132
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
74242
75133
  if (!meshId) return { success: false, error: "meshId required" };
74243
75134
  const requestedNodeIds = Array.isArray(args?.nodeIds) ? args.nodeIds.filter((v) => typeof v === "string" && v.trim().length > 0).map((v) => v.trim()) : void 0;
74244
- return this.batchRefineMeshNodes(meshId, requestedNodeIds, args);
75135
+ const isDryRun = args?.dryRun !== false && args?.execute !== true;
75136
+ if (isDryRun) return this.batchRefineMeshNodes(meshId, requestedNodeIds, args);
75137
+ return this.startMeshRefineBatchJob(meshId, requestedNodeIds, args);
74245
75138
  }
74246
75139
  case "remove_mesh_node": {
74247
75140
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
@@ -75921,6 +76814,7 @@ ${ptyResult.output.slice(-2e3)}`);
75921
76814
  return h.toString(36);
75922
76815
  }
75923
76816
  };
76817
+ init_build_info();
75924
76818
  init_logger();
75925
76819
  init_debug_config();
75926
76820
  var DEFAULT_DAEMON_PORT2 = 19222;