@adhdev/daemon-standalone 1.0.58-rc.3 → 1.0.58-rc.4

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
@@ -36831,10 +36831,10 @@ var require_dist3 = __commonJS({
36831
36831
  }
36832
36832
  function getDaemonBuildInfo() {
36833
36833
  if (cached2) return cached2;
36834
- const commit = readInjected(true ? "789e0aa08f214c5b059b3d12eff74e412488db3b" : void 0) ?? "unknown";
36835
- const commitShort = readInjected(true ? "789e0aa0" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36836
- const version2 = readInjected(true ? "1.0.58-rc.3" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36837
- const builtAt = readInjected(true ? "2026-08-23T01:04:20.549Z" : void 0);
36834
+ const commit = readInjected(true ? "388d6e74b391ae9afd94d860aed141063297dca9" : void 0) ?? "unknown";
36835
+ const commitShort = readInjected(true ? "388d6e74" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36836
+ const version2 = readInjected(true ? "1.0.58-rc.4" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36837
+ const builtAt = readInjected(true ? "2026-08-23T04:11:22.908Z" : void 0);
36838
36838
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36839
36839
  return cached2;
36840
36840
  }
@@ -58268,6 +58268,7 @@ Valid status values: \`completed\` | \`failed\` | \`blocked\` | \`partial\`.`;
58268
58268
  "task_approval_needed",
58269
58269
  "task_question_pending",
58270
58270
  "p2p_dispatch_failed",
58271
+ "dispatch_failed",
58271
58272
  "dispatch_duplicate_rebound",
58272
58273
  "queue_hold_hard_deadline"
58273
58274
  ]);
@@ -69057,6 +69058,146 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
69057
69058
  CLASSIFIED_DIFFICULTIES = ["easy", "medium", "difficult"];
69058
69059
  }
69059
69060
  });
69061
+ function normalizeProviderPriority2(policy) {
69062
+ const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
69063
+ if (!Array.isArray(raw)) return [];
69064
+ const seen = /* @__PURE__ */ new Set();
69065
+ return raw.map((type2) => typeof type2 === "string" ? type2.trim() : "").filter(Boolean).filter((type2) => {
69066
+ if (seen.has(type2)) return false;
69067
+ seen.add(type2);
69068
+ return true;
69069
+ });
69070
+ }
69071
+ function isTerminalSessionStatus(status) {
69072
+ return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
69073
+ }
69074
+ function isIdleSessionState3(state) {
69075
+ const status = readNonEmptyString(state?.status).toLowerCase();
69076
+ if (isTerminalSessionStatus(status)) return false;
69077
+ return status === "idle" || state?.activeChat?.status === "waiting_input";
69078
+ }
69079
+ function sessionStateLooksActive(state) {
69080
+ const status = readNonEmptyString(state?.status).toLowerCase();
69081
+ const chatStatus = readNonEmptyString(state?.activeChat?.status).toLowerCase();
69082
+ const active = /* @__PURE__ */ new Set(["generating", "streaming", "no_progress", "long_generating", "working", "starting", "waiting_approval"]);
69083
+ return active.has(status) || active.has(chatStatus);
69084
+ }
69085
+ function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
69086
+ if (nodeHasActiveAssignment(meshId, nodeId)) return true;
69087
+ return components.instanceManager.getByCategory("cli").some((inst) => {
69088
+ const state = inst.getState();
69089
+ const settings = state.settings || {};
69090
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69091
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69092
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69093
+ const sessionId = readNonEmptyString(state.instanceId);
69094
+ if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
69095
+ return sessionStateLooksActive(state);
69096
+ });
69097
+ }
69098
+ function isLaunchableNode(node) {
69099
+ if (!node || node.status === "disabled" || node.status === "removed") return false;
69100
+ if (shouldDeferDispatchForBootstrap(node)) {
69101
+ return false;
69102
+ }
69103
+ return isMeshNodeHealthLaunchable(node);
69104
+ }
69105
+ function isLocalAutoLaunchNode(node) {
69106
+ const daemonId = readNonEmptyString(node?.daemonId);
69107
+ const machineId = readNonEmptyString(node?.machineId);
69108
+ const appConfig = loadConfig2();
69109
+ const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
69110
+ const daemonMatchesLocal = !daemonId || daemonIdsEquivalent(daemonId, localMachineId);
69111
+ const machineMatchesLocal = !machineId || daemonIdsEquivalent(machineId, localMachineId);
69112
+ if (node?.isLocalWorktree === true) {
69113
+ return daemonMatchesLocal && machineMatchesLocal;
69114
+ }
69115
+ if (daemonId || machineId) {
69116
+ return daemonMatchesLocal && machineMatchesLocal;
69117
+ }
69118
+ return true;
69119
+ }
69120
+ function isSessionActivelyGenerating(components, sessionId) {
69121
+ if (!sessionId) return false;
69122
+ const state = components.instanceManager?.getInstance?.(sessionId)?.getState?.();
69123
+ if (!state) return false;
69124
+ return sessionStateLooksActive(state);
69125
+ }
69126
+ function resolveSessionBusyVerdict(components, sessionId) {
69127
+ if (!sessionId) return "UNKNOWN";
69128
+ try {
69129
+ const instances = components.instanceManager?.getByCategory?.("cli") || [];
69130
+ const inst = instances.find((i) => {
69131
+ const sid = readNonEmptyString(i?.getState?.().instanceId);
69132
+ return sid && sessionIdsEquivalent(sid, sessionId);
69133
+ });
69134
+ if (!inst) return "UNKNOWN";
69135
+ const state = inst.getState?.();
69136
+ if (!state) return "UNKNOWN";
69137
+ return sessionStateLooksActive(state) ? "GENERATING" : "IDLE_CONFIRMED";
69138
+ } catch {
69139
+ return "UNKNOWN";
69140
+ }
69141
+ }
69142
+ function liveSessionCountForNode(components, meshId, nodeId) {
69143
+ const localInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
69144
+ const state = inst.getState();
69145
+ const settings = state.settings || {};
69146
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69147
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69148
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69149
+ const status = readNonEmptyString(state.status).toLowerCase();
69150
+ return !isTerminalSessionStatus(status);
69151
+ });
69152
+ let count = localInstances.length;
69153
+ const localSessionIds = localInstances.map((inst) => readNonEmptyString(inst.getState().instanceId)).filter(Boolean);
69154
+ for (const sid of inWindowAutoLaunchSessionIdsForNode(meshId, nodeId)) {
69155
+ if (!localSessionIds.some((local) => sessionIdsEquivalent(local, sid))) count += 1;
69156
+ }
69157
+ return count;
69158
+ }
69159
+ function nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node) {
69160
+ if (inWindowAutoLaunchSessionIdsForNode(meshId, nodeId).length > 0) return true;
69161
+ const busySessionIds = new Set(
69162
+ getQueue(meshId, { status: ["assigned"] }).filter((task2) => daemonIdsEquivalent(task2.assignedNodeId, nodeId)).map((task2) => readNonEmptyString(task2.assignedSessionId)).filter(Boolean)
69163
+ );
69164
+ return components.instanceManager.getByCategory("cli").some((inst) => {
69165
+ const state = inst.getState();
69166
+ const settings = state.settings || {};
69167
+ if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69168
+ if (readNonEmptyString(settings.meshCoordinatorFor) === meshId) return false;
69169
+ const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69170
+ if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69171
+ const status = readNonEmptyString(state.status).toLowerCase();
69172
+ if (isTerminalSessionStatus(status)) return false;
69173
+ const sessionId = readNonEmptyString(state.instanceId);
69174
+ if (sessionId && busySessionIds.has(sessionId)) return false;
69175
+ const sessionProviderType = state.type || readNonEmptyString(settings.providerType);
69176
+ if (task.requiredTags?.length) {
69177
+ if (sessionProviderType && !nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, sessionProviderType))) {
69178
+ return false;
69179
+ }
69180
+ }
69181
+ const allowance = allowedClassifiedDifficultiesForSession(node, resolveNodeCapabilitySlots(node, meshId), sessionProviderType, readSessionModel(state));
69182
+ if (!taskMeetsSessionDifficultyFloor(task, allowance)) return false;
69183
+ return true;
69184
+ });
69185
+ }
69186
+ var init_mesh_candidacy_predicates = __esm2({
69187
+ "src/mesh/mesh-candidacy-predicates.ts"() {
69188
+ "use strict";
69189
+ init_config();
69190
+ init_mesh_work_queue();
69191
+ init_dist();
69192
+ init_mesh_events_utils();
69193
+ init_mesh_node_identity();
69194
+ init_worktree_bootstrap_config();
69195
+ init_mesh_autolaunch_integrity();
69196
+ init_mesh_scheduling_fitness();
69197
+ init_mesh_node_slots();
69198
+ init_mesh_difficulty_floor();
69199
+ }
69200
+ });
69060
69201
  function localCoordinatorDaemonId2() {
69061
69202
  return canonicalDaemonId(readNonEmptyString(loadConfig2().machineId));
69062
69203
  }
@@ -69323,6 +69464,12 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
69323
69464
  }
69324
69465
  try {
69325
69466
  appendLedgerEntry(ctx.meshId, {
69467
+ // 'dispatch_failed' is a real MeshLedgerKind and a member of
69468
+ // TASK_LIFECYCLE_LEDGER_KINDS, so appendLedgerEntry derives the top-level
69469
+ // taskId from payload.taskId below. It spent its whole life as
69470
+ // `as any` — off the union, hence off the lifecycle set, hence written to
69471
+ // the indexed SQLite task_id column as NULL and unreachable by the
69472
+ // kind+taskId join every reader uses. Do not reintroduce the cast.
69326
69473
  kind: "dispatch_failed",
69327
69474
  nodeId: ctx.nodeId,
69328
69475
  sessionId: ctx.sessionId,
@@ -69668,65 +69815,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
69668
69815
  if (now >= until) autoLaunchCooldownUntil.delete(key2);
69669
69816
  }
69670
69817
  }
69671
- function normalizeProviderPriority2(policy) {
69672
- const raw = policy && typeof policy === "object" && !Array.isArray(policy) ? policy.providerPriority : void 0;
69673
- if (!Array.isArray(raw)) return [];
69674
- const seen = /* @__PURE__ */ new Set();
69675
- return raw.map((type2) => typeof type2 === "string" ? type2.trim() : "").filter(Boolean).filter((type2) => {
69676
- if (seen.has(type2)) return false;
69677
- seen.add(type2);
69678
- return true;
69679
- });
69680
- }
69681
- function isTerminalSessionStatus(status) {
69682
- return ["stopped", "failed", "terminated", "exited", "closed"].includes(status);
69683
- }
69684
- function isIdleSessionState3(state) {
69685
- const status = readNonEmptyString(state?.status).toLowerCase();
69686
- if (isTerminalSessionStatus(status)) return false;
69687
- return status === "idle" || state?.activeChat?.status === "waiting_input";
69688
- }
69689
- function sessionStateLooksActive(state) {
69690
- const status = readNonEmptyString(state?.status).toLowerCase();
69691
- const chatStatus = readNonEmptyString(state?.activeChat?.status).toLowerCase();
69692
- const active = /* @__PURE__ */ new Set(["generating", "streaming", "no_progress", "long_generating", "working", "starting", "waiting_approval"]);
69693
- return active.has(status) || active.has(chatStatus);
69694
- }
69695
- function nodeHasActiveMeshWork(components, meshId, nodeId, currentSessionId) {
69696
- if (nodeHasActiveAssignment(meshId, nodeId)) return true;
69697
- return components.instanceManager.getByCategory("cli").some((inst) => {
69698
- const state = inst.getState();
69699
- const settings = state.settings || {};
69700
- if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69701
- const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69702
- if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69703
- const sessionId = readNonEmptyString(state.instanceId);
69704
- if (currentSessionId && sessionIdsEquivalent(sessionId, currentSessionId) && isIdleSessionState3(state)) return false;
69705
- return sessionStateLooksActive(state);
69706
- });
69707
- }
69708
- function isLaunchableNode(node) {
69709
- if (!node || node.status === "disabled" || node.status === "removed") return false;
69710
- if (shouldDeferDispatchForBootstrap(node)) {
69711
- return false;
69712
- }
69713
- return isMeshNodeHealthLaunchable(node);
69714
- }
69715
- function isLocalAutoLaunchNode(node) {
69716
- const daemonId = readNonEmptyString(node?.daemonId);
69717
- const machineId = readNonEmptyString(node?.machineId);
69718
- const appConfig = loadConfig2();
69719
- const localMachineId = readNonEmptyString(appConfig.machineId) || readNonEmptyString(appConfig.registeredMachineId);
69720
- const daemonMatchesLocal = !daemonId || daemonIdsEquivalent(daemonId, localMachineId);
69721
- const machineMatchesLocal = !machineId || daemonIdsEquivalent(machineId, localMachineId);
69722
- if (node?.isLocalWorktree === true) {
69723
- return daemonMatchesLocal && machineMatchesLocal;
69724
- }
69725
- if (daemonId || machineId) {
69726
- return daemonMatchesLocal && machineMatchesLocal;
69727
- }
69728
- return true;
69729
- }
69730
69818
  function resolveAutoLaunchTarget(components, node) {
69731
69819
  if (isLocalAutoLaunchNode(node)) return { mode: "local" };
69732
69820
  const daemonId = readMeshNodeDaemonId(node ?? {});
@@ -69736,72 +69824,6 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
69736
69824
  if (!coordinatorDaemonId) return { mode: "skip", reason: "remote_auto_launch_no_coordinator_daemon_id" };
69737
69825
  return { mode: "remote", daemonId, coordinatorDaemonId };
69738
69826
  }
69739
- function isSessionActivelyGenerating(components, sessionId) {
69740
- if (!sessionId) return false;
69741
- const state = components.instanceManager?.getInstance?.(sessionId)?.getState?.();
69742
- if (!state) return false;
69743
- return sessionStateLooksActive(state);
69744
- }
69745
- function resolveSessionBusyVerdict(components, sessionId) {
69746
- if (!sessionId) return "UNKNOWN";
69747
- try {
69748
- const instances = components.instanceManager?.getByCategory?.("cli") || [];
69749
- const inst = instances.find((i) => {
69750
- const sid = readNonEmptyString(i?.getState?.().instanceId);
69751
- return sid && sessionIdsEquivalent(sid, sessionId);
69752
- });
69753
- if (!inst) return "UNKNOWN";
69754
- const state = inst.getState?.();
69755
- if (!state) return "UNKNOWN";
69756
- return sessionStateLooksActive(state) ? "GENERATING" : "IDLE_CONFIRMED";
69757
- } catch {
69758
- return "UNKNOWN";
69759
- }
69760
- }
69761
- function liveSessionCountForNode(components, meshId, nodeId) {
69762
- const localInstances = components.instanceManager.getByCategory("cli").filter((inst) => {
69763
- const state = inst.getState();
69764
- const settings = state.settings || {};
69765
- if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69766
- const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69767
- if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69768
- const status = readNonEmptyString(state.status).toLowerCase();
69769
- return !isTerminalSessionStatus(status);
69770
- });
69771
- let count = localInstances.length;
69772
- const localSessionIds = localInstances.map((inst) => readNonEmptyString(inst.getState().instanceId)).filter(Boolean);
69773
- for (const sid of inWindowAutoLaunchSessionIdsForNode(meshId, nodeId)) {
69774
- if (!localSessionIds.some((local) => sessionIdsEquivalent(local, sid))) count += 1;
69775
- }
69776
- return count;
69777
- }
69778
- function nodeHasLiveSessionPendingClaim(components, meshId, nodeId, task, node) {
69779
- if (inWindowAutoLaunchSessionIdsForNode(meshId, nodeId).length > 0) return true;
69780
- const busySessionIds = new Set(
69781
- getQueue(meshId, { status: ["assigned"] }).filter((task2) => daemonIdsEquivalent(task2.assignedNodeId, nodeId)).map((task2) => readNonEmptyString(task2.assignedSessionId)).filter(Boolean)
69782
- );
69783
- return components.instanceManager.getByCategory("cli").some((inst) => {
69784
- const state = inst.getState();
69785
- const settings = state.settings || {};
69786
- if (readNonEmptyString(settings.meshNodeFor) !== meshId) return false;
69787
- if (readNonEmptyString(settings.meshCoordinatorFor) === meshId) return false;
69788
- const instNodeId = readNonEmptyString(settings.meshNodeId) || readNonEmptyString(settings.nodeId);
69789
- if (!daemonIdsEquivalent(instNodeId, nodeId)) return false;
69790
- const status = readNonEmptyString(state.status).toLowerCase();
69791
- if (isTerminalSessionStatus(status)) return false;
69792
- const sessionId = readNonEmptyString(state.instanceId);
69793
- if (sessionId && busySessionIds.has(sessionId)) return false;
69794
- const sessionProviderType = state.type || readNonEmptyString(settings.providerType);
69795
- if (task.requiredTags?.length) {
69796
- if (sessionProviderType && !nodeSatisfiesRequiredTags(task.requiredTags, buildMeshNodeCapabilityTags(node, sessionProviderType))) {
69797
- return false;
69798
- }
69799
- }
69800
- const allowance = allowedClassifiedDifficultiesForSession(node, resolveNodeCapabilitySlots(node, meshId), sessionProviderType, readSessionModel(state));
69801
- if (!taskMeetsSessionDifficultyFloor(task, allowance)) return false;
69802
- return true;
69803
- });
69804
- }
69805
69827
  function markAutoLaunch(meshId, taskId, args) {
69806
69828
  const reason = args.reason || args.error;
69807
69829
  const difficultyFloorSkip = args.status === "skipped" && isDifficultyFloorWaitReason(reason);
@@ -70541,6 +70563,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
70541
70563
  init_mesh_routing_decision();
70542
70564
  init_mesh_autolaunch_integrity();
70543
70565
  init_mesh_difficulty_floor();
70566
+ init_mesh_candidacy_predicates();
70544
70567
  init_mesh_difficulty_floor();
70545
70568
  init_mesh_auto_fast_forward();
70546
70569
  init_mesh_skip_notify();
@@ -81644,6 +81667,49 @@ ${cleanBody}`;
81644
81667
  }
81645
81668
  return false;
81646
81669
  }
81670
+ function probeSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
81671
+ if (!baseCommit || !branchCommit) return "undeterminable";
81672
+ if (baseCommit === branchCommit) return true;
81673
+ return probeGitAncestry(submoduleRepoPath, baseCommit, branchCommit);
81674
+ }
81675
+ function isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
81676
+ return probeSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) === true;
81677
+ }
81678
+ function buildGitlinkFastForwardUndeterminableWarning(label, gitlinks) {
81679
+ const entries = gitlinks.filter((entry) => entry.fastForwardUndeterminable);
81680
+ if (entries.length === 0) return void 0;
81681
+ const detail = entries.map((entry) => `${entry.path} (base=${(entry.baseCommit || "?").slice(0, 12)} branch=${(entry.branchCommit || "?").slice(0, 12)})`).join(", ");
81682
+ return `[Refinery] Could NOT determine submodule gitlink fast-forward for ${label} \u2014 ancestry probe had NO ANSWER (a gitlink commit is missing from the local submodule object store, or the submodule checkout is absent). "undeterminable", NOT "diverged": ${detail}`;
81683
+ }
81684
+ function warnGitlinkFastForwardUndeterminable(label, gitlinks) {
81685
+ const message = buildGitlinkFastForwardUndeterminableWarning(label, gitlinks);
81686
+ if (message) LOG2.warn("Mesh", message);
81687
+ }
81688
+ function readChangedPathKinds(repoRoot, fromRef, toRef) {
81689
+ try {
81690
+ const output = (0, import_node_child_process11.execFileSync)(GIT4, ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
81691
+ cwd: repoRoot,
81692
+ encoding: "utf8",
81693
+ maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
81694
+ });
81695
+ const result = [];
81696
+ const seen = /* @__PURE__ */ new Set();
81697
+ for (const line of output.split("\n")) {
81698
+ if (!line.trim()) continue;
81699
+ const metaAndPath = line.split(" ");
81700
+ const meta3 = metaAndPath[0] || "";
81701
+ const path60 = metaAndPath[metaAndPath.length - 1]?.trim();
81702
+ if (!path60 || seen.has(path60)) continue;
81703
+ seen.add(path60);
81704
+ const parts = meta3.split(/\s+/);
81705
+ const isGitlink = !!(parts[0]?.includes("160000") || parts[1]?.includes("160000"));
81706
+ result.push({ path: path60, isGitlink });
81707
+ }
81708
+ return result;
81709
+ } catch {
81710
+ return [];
81711
+ }
81712
+ }
81647
81713
  var fs51;
81648
81714
  var import_node_child_process11;
81649
81715
  var import_path17;
@@ -81729,7 +81795,16 @@ ${cleanBody}`;
81729
81795
  }
81730
81796
  return void 0;
81731
81797
  }
81732
- function convergeDivergedSubmoduleGitlinks(worktreeRoot, baseRepoRoot, baseHead, branchHead) {
81798
+ function isCommitReachableFromRemoteMain(submoduleRepoPath, commit, remoteMainRef) {
81799
+ if (!remoteMainRef) return false;
81800
+ try {
81801
+ (0, import_node_child_process12.execFileSync)(GIT4, ["merge-base", "--is-ancestor", commit, remoteMainRef], { cwd: submoduleRepoPath, stdio: "ignore" });
81802
+ return true;
81803
+ } catch {
81804
+ return false;
81805
+ }
81806
+ }
81807
+ function convergeDivergedSubmoduleGitlinks(worktreeRoot, baseRepoRoot, baseHead, branchHead, options = {}) {
81733
81808
  const changed = readChangedGitlinkPaths(worktreeRoot, baseHead, branchHead);
81734
81809
  if (changed.length === 0) {
81735
81810
  return { converged: false, reason: "no_changed_gitlinks", resolutions: [], gitlinks: [] };
@@ -81781,6 +81856,30 @@ ${cleanBody}`;
81781
81856
  resolutions.push({ path: path60, baseCommit, branchCommit, rebasedCommit: publishedEquivalent });
81782
81857
  continue;
81783
81858
  }
81859
+ const branchAlreadyPublished = isCommitReachableFromRemoteMain(submoduleRepoPath, branchCommit, remoteMainRef);
81860
+ const willMintUnpublishedCommit = !branchAlreadyPublished;
81861
+ const wouldConflict = (() => {
81862
+ try {
81863
+ (0, import_node_child_process12.execFileSync)(GIT4, ["merge-tree", "--write-tree", baseCommit, branchCommit], {
81864
+ cwd: submoduleRepoPath,
81865
+ encoding: "utf8",
81866
+ stdio: ["ignore", "pipe", "pipe"]
81867
+ });
81868
+ return false;
81869
+ } catch {
81870
+ return true;
81871
+ }
81872
+ })();
81873
+ if (willMintUnpublishedCommit && !wouldConflict && options.allowAutoPublishSubmoduleMainCommits !== true) {
81874
+ gitlinks.push({
81875
+ path: path60,
81876
+ baseCommit,
81877
+ branchCommit,
81878
+ action: "publish_required_before_rebase",
81879
+ remoteMainRef
81880
+ });
81881
+ return { converged: false, reason: "submodule_publish_required", resolutions: [], gitlinks };
81882
+ }
81784
81883
  let rebasedCommit;
81785
81884
  try {
81786
81885
  (0, import_node_child_process12.execFileSync)(GIT4, ["checkout", "-q", "--detach", branchCommit], { cwd: submoduleRepoPath, stdio: ["ignore", "ignore", "pipe"] });
@@ -81820,7 +81919,14 @@ ${cleanBody}`;
81820
81919
  gitlinks.push({ path: path60, baseCommit, branchCommit, rebasedCommit, action: "rebase_dropped_branch_commits" });
81821
81920
  return { converged: false, reason: "rebase_dropped_branch_commits", resolutions: [], gitlinks };
81822
81921
  }
81823
- gitlinks.push({ path: path60, baseCommit, branchCommit, rebasedCommit, action: "rebased" });
81922
+ gitlinks.push({
81923
+ path: path60,
81924
+ baseCommit,
81925
+ branchCommit,
81926
+ rebasedCommit,
81927
+ action: "rebased",
81928
+ ...willMintUnpublishedCommit ? { mintedUnpublishedCommit: true, remoteMainRef } : {}
81929
+ });
81824
81930
  resolutions.push({ path: path60, baseCommit, branchCommit, rebasedCommit });
81825
81931
  }
81826
81932
  if (!sawDiverged) {
@@ -81829,6 +81935,31 @@ ${cleanBody}`;
81829
81935
  }
81830
81936
  return { converged: resolutions.length > 0, resolutions, gitlinks };
81831
81937
  }
81938
+ function describeSubmodulePublishRequired(nodeId, gitlinks) {
81939
+ const pending = gitlinks.filter((g) => g.action === "publish_required_before_rebase");
81940
+ return `[Refinery] Refusing to auto-converge diverged submodule gitlink(s) for node ${nodeId} \u2014 the rebase would create a NEW submodule commit that is not reachable from the submodule remote, and allowAutoPublishSubmoduleMainCommits is disabled, so the reachability gate would reject it. The branch was left untouched. NEXT STEP: publish the branch-side submodule commit(s) to submodule origin main, then rerun mesh_refine_node \u2014 do NOT rebase: ` + pending.map((g) => `${g.path} (branch=${(g.branchCommit || "?").slice(0, 12)} base=${(g.baseCommit || "?").slice(0, 12)} remote=${g.remoteMainRef || "unresolved"})`).join(", ");
81941
+ }
81942
+ function describeSubmoduleConvergeDecline(nodeId, reason, gitlinks) {
81943
+ if (reason === "submodule_publish_required") {
81944
+ return describeSubmodulePublishRequired(nodeId, gitlinks);
81945
+ }
81946
+ if (reason === "submodule_commit_unavailable") {
81947
+ return `[Refinery] Could NOT determine submodule gitlink divergence for node ${nodeId} \u2014 a gitlink commit is missing from the local submodule object store (auto-converge could not run): ` + gitlinks.filter((g) => g.action === "skipped_commit_unavailable").map((g) => `${g.path} (missing: ${(g.unavailable || []).join("+") || "unknown"}; base=${(g.baseCommit || "?").slice(0, 12)} branch=${(g.branchCommit || "?").slice(0, 12)})`).join(", ");
81948
+ }
81949
+ return void 0;
81950
+ }
81951
+ function buildSubmoduleConvergeDeclineDetails(reason, autoPublishEnabled) {
81952
+ if (reason !== "submodule_publish_required") return {};
81953
+ return {
81954
+ recommendedAction: SUBMODULE_PUBLISH_REQUIRED_RECOMMENDED_ACTION,
81955
+ autoPublishAllowed: autoPublishEnabled
81956
+ };
81957
+ }
81958
+ function describeMintedUnpublishedCommits(nodeId, gitlinks, autoPublishEnabled) {
81959
+ const minted = gitlinks.filter((g) => g.mintedUnpublishedCommit);
81960
+ if (minted.length === 0) return void 0;
81961
+ return `[Refinery] Node ${nodeId}: the convergence MINTED ${minted.length} new submodule commit(s) not reachable from the submodule remote (allowed because allowAutoPublishSubmoduleMainCommits=${autoPublishEnabled}); a publish is still required before merge: ` + minted.map((g) => `${g.path}\u2192${(g.rebasedCommit || "?").slice(0, 12)} (remote=${g.remoteMainRef || "unresolved"})`).join(", ");
81962
+ }
81832
81963
  function rootRebaseResolvingGitlinks(worktreeRoot, baseHead, resolutions) {
81833
81964
  const resolveByPath = new Map(resolutions.map((r) => [r.path, r.rebasedCommit]));
81834
81965
  const runRebase = (args) => {
@@ -81910,6 +82041,7 @@ ${cleanBody}`;
81910
82041
  var fs52;
81911
82042
  var import_path18;
81912
82043
  var import_node_child_process12;
82044
+ var SUBMODULE_PUBLISH_REQUIRED_RECOMMENDED_ACTION;
81913
82045
  var init_mesh_refine_submodule_converge = __esm2({
81914
82046
  "src/mesh/mesh-refine-submodule-converge.ts"() {
81915
82047
  "use strict";
@@ -81917,6 +82049,7 @@ ${cleanBody}`;
81917
82049
  import_path18 = require("path");
81918
82050
  import_node_child_process12 = require("child_process");
81919
82051
  init_mesh_refine_gitlink_utils();
82052
+ SUBMODULE_PUBLISH_REQUIRED_RECOMMENDED_ACTION = "Publish the branch-side submodule commit(s) to submodule origin main, then rerun mesh_refine_node. Do NOT retry the rebase: the branch was intentionally left unrewritten because a rebase here would mint a submodule commit the reachability gate must reject.";
81920
82053
  }
81921
82054
  });
81922
82055
  function truncateValidationOutput(value) {
@@ -82059,6 +82192,7 @@ ${mergeTreeErr?.stderr || ""}`;
82059
82192
  if (!isSubmoduleConflict) throw mergeTreeErr;
82060
82193
  const evaluation = evaluateGitlinkTrivialFastForward(repoRoot, baseHead, branchHead);
82061
82194
  if (!evaluation.trivial) {
82195
+ warnGitlinkFastForwardUndeterminable("patch-equivalence gate", evaluation.gitlinks);
82062
82196
  return {
82063
82197
  status: "failed",
82064
82198
  equivalent: false,
@@ -82152,14 +82286,6 @@ ${e?.stderr || ""}`
82152
82286
  maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES,
82153
82287
  windowsHide: true
82154
82288
  });
82155
- const gitOk = (args) => {
82156
- try {
82157
- git3(args);
82158
- return true;
82159
- } catch {
82160
- return false;
82161
- }
82162
- };
82163
82289
  let ahead = 0;
82164
82290
  let behind = 0;
82165
82291
  try {
@@ -82171,8 +82297,13 @@ ${e?.stderr || ""}`
82171
82297
  }
82172
82298
  evidence.ahead = ahead;
82173
82299
  evidence.behind = behind;
82174
- const baseIsAncestor = gitOk(["merge-base", "--is-ancestor", targetBaseRef, branchHead]);
82175
- evidence.baseDiverged = !baseIsAncestor;
82300
+ const baseAncestry = probeGitAncestry(repoRoot, targetBaseRef, branchHead);
82301
+ const baseIsAncestor = baseAncestry === true;
82302
+ if (baseAncestry === "undeterminable") {
82303
+ evidence.baseAncestryUndeterminable = true;
82304
+ } else {
82305
+ evidence.baseDiverged = !baseAncestry;
82306
+ }
82176
82307
  let diffStat = "";
82177
82308
  try {
82178
82309
  if (summary.mergedTree) {
@@ -82253,6 +82384,14 @@ ${e?.stderr || ""}`
82253
82384
  evidence
82254
82385
  };
82255
82386
  }
82387
+ if (baseAncestry === "undeterminable") {
82388
+ return {
82389
+ detailedReason: "base_ancestry_undeterminable",
82390
+ detailedReasonDescription: `Could NOT determine whether HEAD descends ${targetBaseRef} \u2014 the ancestry probe had no answer (${targetBaseRef} and/or ${branchHead.slice(0, 12)} does not resolve in ${repoRoot}). This is "undeterminable", NOT "diverged": the ahead/behind counts above are unmeasured, not zero.`,
82391
+ recommendedAction: `Make the probe answerable before judging: fetch/verify that ${targetBaseRef} and the branch head both resolve in ${repoRoot} (e.g. git fetch origin, git rev-parse --verify), then retry mesh_refine_node. Do NOT rebase on the strength of this result \u2014 no divergence has been measured.`,
82392
+ evidence
82393
+ };
82394
+ }
82256
82395
  if (!baseIsAncestor) {
82257
82396
  return {
82258
82397
  detailedReason: "base_divergence",
@@ -82298,6 +82437,7 @@ ${mergeTreeErr?.stderr || ""}`;
82298
82437
  if (!isSubmoduleConflict) throw mergeTreeErr;
82299
82438
  const evaluation = evaluateGitlinkTrivialFastForward(repoRoot, ref, worktreeHead);
82300
82439
  if (!evaluation.trivial) {
82440
+ warnGitlinkFastForwardUndeterminable(`containment check for ${ref}`, evaluation.gitlinks);
82301
82441
  return {
82302
82442
  contained: false,
82303
82443
  ref,
@@ -82427,44 +82567,6 @@ ${mergeTreeErr?.stderr || ""}`;
82427
82567
  }).trim();
82428
82568
  return out;
82429
82569
  }
82430
- function isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) {
82431
- if (!baseCommit || !branchCommit) return false;
82432
- if (baseCommit === branchCommit) return true;
82433
- try {
82434
- if (!fs53.existsSync(submoduleRepoPath)) return false;
82435
- (0, import_node_child_process13.execFileSync)(GIT4, ["cat-file", "-e", `${baseCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
82436
- (0, import_node_child_process13.execFileSync)(GIT4, ["cat-file", "-e", `${branchCommit}^{commit}`], { cwd: submoduleRepoPath, stdio: "ignore" });
82437
- (0, import_node_child_process13.execFileSync)(GIT4, ["merge-base", "--is-ancestor", baseCommit, branchCommit], { cwd: submoduleRepoPath, stdio: "ignore" });
82438
- return true;
82439
- } catch {
82440
- return false;
82441
- }
82442
- }
82443
- function readChangedPathKinds(repoRoot, fromRef, toRef) {
82444
- try {
82445
- const output = (0, import_node_child_process13.execFileSync)(GIT4, ["diff", "--raw", "--no-abbrev", fromRef, toRef], {
82446
- cwd: repoRoot,
82447
- encoding: "utf8",
82448
- maxBuffer: REFINE_PATCH_EQUIVALENCE_OUTPUT_LIMIT_BYTES
82449
- });
82450
- const result = [];
82451
- const seen = /* @__PURE__ */ new Set();
82452
- for (const line of output.split("\n")) {
82453
- if (!line.trim()) continue;
82454
- const metaAndPath = line.split(" ");
82455
- const meta3 = metaAndPath[0] || "";
82456
- const path60 = metaAndPath[metaAndPath.length - 1]?.trim();
82457
- if (!path60 || seen.has(path60)) continue;
82458
- seen.add(path60);
82459
- const parts = meta3.split(/\s+/);
82460
- const isGitlink = !!(parts[0]?.includes("160000") || parts[1]?.includes("160000"));
82461
- result.push({ path: path60, isGitlink });
82462
- }
82463
- return result;
82464
- } catch {
82465
- return [];
82466
- }
82467
- }
82468
82570
  function collectFastForwardGitlinkPaths(repoRoot, baseHead, branchHead) {
82469
82571
  return readChangedGitlinkPaths(repoRoot, baseHead, branchHead).filter((path60) => {
82470
82572
  const baseCommit = readTreeObject(repoRoot, baseHead, path60);
@@ -82497,19 +82599,27 @@ ${mergeTreeErr?.stderr || ""}`;
82497
82599
  const baseCommit = readTreeObject(repoRoot, baseHead, path60);
82498
82600
  const branchCommit = readTreeObject(repoRoot, branchHead, path60);
82499
82601
  const submoduleRepoPath = (0, import_path19.resolve)(repoRoot, path60);
82500
- const fastForward = !!baseCommit && !!branchCommit && isSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit);
82501
- return { path: path60, baseCommit, branchCommit, fastForward };
82602
+ const probe = !!baseCommit && !!branchCommit ? probeSubmoduleFastForward(submoduleRepoPath, baseCommit, branchCommit) : "undeterminable";
82603
+ return {
82604
+ path: path60,
82605
+ baseCommit,
82606
+ branchCommit,
82607
+ fastForward: probe === true,
82608
+ ...probe === "undeterminable" ? { fastForwardUndeterminable: true } : {}
82609
+ };
82502
82610
  });
82503
82611
  if (changedGitlinks.length === 0) {
82504
82612
  return { trivial: false, reason: "no_changed_gitlinks", gitlinks: changedGitlinks };
82505
82613
  }
82506
- const nonFastForward = changedGitlinks.filter((entry) => !entry.fastForward);
82507
- if (nonFastForward.length > 0) {
82508
- return {
82509
- trivial: false,
82510
- reason: `diverged_gitlinks:${nonFastForward.map((entry) => entry.path).join(",")}`,
82511
- gitlinks: changedGitlinks
82512
- };
82614
+ const undeterminable = changedGitlinks.filter((entry) => entry.fastForwardUndeterminable);
82615
+ const diverged = changedGitlinks.filter((entry) => !entry.fastForward && !entry.fastForwardUndeterminable);
82616
+ if (undeterminable.length > 0 || diverged.length > 0) {
82617
+ const parts = [];
82618
+ if (diverged.length > 0) parts.push(`diverged_gitlinks:${diverged.map((entry) => entry.path).join(",")}`);
82619
+ if (undeterminable.length > 0) {
82620
+ parts.push(`undeterminable_gitlinks:${undeterminable.map((entry) => entry.path).join(",")}`);
82621
+ }
82622
+ return { trivial: false, reason: parts.join(" "), gitlinks: changedGitlinks };
82513
82623
  }
82514
82624
  let mergeBase = "";
82515
82625
  try {
@@ -83235,6 +83345,7 @@ ${mergeTreeErr?.stderr || ""}`;
83235
83345
  init_mesh_refine_worker_cap();
83236
83346
  init_mesh_refine_gitlink_utils();
83237
83347
  init_mesh_refine_submodule_converge();
83348
+ init_mesh_refine_gitlink_utils();
83238
83349
  REFINE_VALIDATION_TIMEOUT_MS = 12e4;
83239
83350
  REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
83240
83351
  REFINE_VALIDATION_SUMMARY_HEAD_CHARS = 2e3;
@@ -84224,7 +84335,14 @@ ${excerpt}` : "\n--- git output ---\n(none captured)");
84224
84335
  return { kind: "continue", ctx };
84225
84336
  }
84226
84337
  if (submoduleConflict) {
84227
- const converge = convergeDivergedSubmoduleGitlinks(node.workspace, repoRoot, baseHead, branchHead);
84338
+ const convergeAutoPublish = resolveRefineryAutoPublishSubmoduleMainCommits(ctx.mesh, node.workspace);
84339
+ const converge = convergeDivergedSubmoduleGitlinks(
84340
+ node.workspace,
84341
+ repoRoot,
84342
+ baseHead,
84343
+ branchHead,
84344
+ { allowAutoPublishSubmoduleMainCommits: convergeAutoPublish.enabled }
84345
+ );
84228
84346
  if (converge.converged) {
84229
84347
  gitlinkResolutions = converge.resolutions;
84230
84348
  recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "passed", syncStarted, {
@@ -84232,15 +84350,19 @@ ${excerpt}` : "\n--- git output ---\n(none captured)");
84232
84350
  gitlinks: converge.gitlinks
84233
84351
  });
84234
84352
  LOG2.info("Mesh", `[Refinery] Auto-converged diverged submodule gitlink(s) onto base for node ${node.id}: ` + converge.resolutions.map((r) => `${r.path}\u2192${r.rebasedCommit.slice(0, 12)}`).join(", "));
84353
+ const minted = describeMintedUnpublishedCommits(node.id, converge.gitlinks, convergeAutoPublish.enabled);
84354
+ if (minted) LOG2.warn("Mesh", minted);
84235
84355
  } else {
84236
84356
  recordMeshRefineStage(refineStages, "submodule_gitlink_converge", "skipped", syncStarted, {
84237
84357
  reason: "submodule_conflict_defer_to_patch_equivalence",
84238
84358
  convergeReason: converge.reason,
84239
- gitlinks: converge.gitlinks
84359
+ gitlinks: converge.gitlinks,
84360
+ // ★Structural next step, so the coordinator is told to PUBLISH
84361
+ // rather than retry the rebase (retrying re-mints the orphan).
84362
+ ...buildSubmoduleConvergeDeclineDetails(converge.reason, convergeAutoPublish.enabled)
84240
84363
  });
84241
- if (converge.reason === "submodule_commit_unavailable") {
84242
- LOG2.warn("Mesh", `[Refinery] Could NOT determine submodule gitlink divergence for node ${node.id} \u2014 a gitlink commit is missing from the local submodule object store (auto-converge could not run): ` + converge.gitlinks.filter((g) => g.action === "skipped_commit_unavailable").map((g) => `${g.path} (missing: ${(g.unavailable || []).join("+") || "unknown"}; base=${(g.baseCommit || "?").slice(0, 12)} branch=${(g.branchCommit || "?").slice(0, 12)})`).join(", "));
84243
- }
84364
+ const declined = describeSubmoduleConvergeDecline(node.id, converge.reason, converge.gitlinks);
84365
+ if (declined) LOG2.warn("Mesh", declined);
84244
84366
  recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
84245
84367
  ahead: divergence.ahead,
84246
84368
  behind: divergence.behind,
@@ -122246,14 +122368,14 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
122246
122368
  let daemonId;
122247
122369
  if (nodeId) {
122248
122370
  try {
122249
- const [{ getMesh: getMesh2 }, { meshNodeIdMatches: meshNodeIdMatches2 }, { readMeshNodeDaemonId: readMeshNodeDaemonId2 }] = await Promise.all([
122371
+ const [{ getMesh: getMesh2 }, { meshNodeIdMatches: meshNodeIdMatches2 }, { readMeshNodeDaemonId: readMeshNodeDaemonId3 }] = await Promise.all([
122250
122372
  Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports)),
122251
122373
  Promise.resolve().then(() => (init_dist(), dist_exports)),
122252
122374
  Promise.resolve().then(() => (init_mesh_node_identity(), mesh_node_identity_exports))
122253
122375
  ]);
122254
122376
  const mesh = getMesh2(meshId);
122255
122377
  const node = mesh?.nodes?.find((n) => meshNodeIdMatches2(n, nodeId));
122256
- daemonId = node ? readMeshNodeDaemonId2(node) || void 0 : void 0;
122378
+ daemonId = node ? readMeshNodeDaemonId3(node) || void 0 : void 0;
122257
122379
  } catch {
122258
122380
  }
122259
122381
  }