@adhdev/daemon-core 0.9.82-rc.486 → 0.9.82-rc.488

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.mjs CHANGED
@@ -409,10 +409,10 @@ function readInjected(value) {
409
409
  }
410
410
  function getDaemonBuildInfo() {
411
411
  if (cached) return cached;
412
- const commit = readInjected(true ? "cae0f42043ded25e4d3bed93d9f658035c09e007" : void 0) ?? "unknown";
413
- const commitShort = readInjected(true ? "cae0f420" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
- const version = readInjected(true ? "0.9.82-rc.486" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
- const builtAt = readInjected(true ? "2026-07-09T23:19:53.642Z" : void 0);
412
+ const commit = readInjected(true ? "a659fcbbe9641240de42b95804a16b8852c5b4eb" : void 0) ?? "unknown";
413
+ const commitShort = readInjected(true ? "a659fcbb" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
414
+ const version = readInjected(true ? "0.9.82-rc.488" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
415
+ const builtAt = readInjected(true ? "2026-07-10T03:46:52.780Z" : void 0);
416
416
  cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
417
417
  return cached;
418
418
  }
@@ -784,30 +784,41 @@ function isNonRuntimeRootFile(file, policy) {
784
784
  }
785
785
  return false;
786
786
  }
787
+ function classifyChangedFileList(files, policy) {
788
+ if (files.length === 0) {
789
+ return { isDaemonAffecting: true, affectedPackages: [] };
790
+ }
791
+ const pkgs = /* @__PURE__ */ new Set();
792
+ let sawRuntimeAmbiguousNonPackage = false;
793
+ for (const file of files) {
794
+ const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
795
+ if (!match) {
796
+ if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
797
+ continue;
798
+ }
799
+ pkgs.add(match[1]);
800
+ }
801
+ const affectedPackages = [...pkgs].sort();
802
+ const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
803
+ return { isDaemonAffecting: !allBenign, affectedPackages };
804
+ }
787
805
  async function classifyDaemonBuildChange(repoPath, buildCommit, options, policy) {
788
806
  try {
789
807
  const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
790
808
  const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
791
- if (files.length === 0) {
792
- return { isDaemonAffecting: true, affectedPackages: [] };
793
- }
794
- const pkgs = /* @__PURE__ */ new Set();
795
- let sawRuntimeAmbiguousNonPackage = false;
796
- for (const file of files) {
797
- const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
798
- if (!match) {
799
- if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
800
- continue;
801
- }
802
- pkgs.add(match[1]);
803
- }
804
- const affectedPackages = [...pkgs].sort();
805
- const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
806
- return { isDaemonAffecting: !allBenign, affectedPackages };
809
+ return classifyChangedFileList(files, policy);
807
810
  } catch {
808
811
  return { isDaemonAffecting: true, affectedPackages: [] };
809
812
  }
810
813
  }
814
+ async function classifyChangedPackages(repoPath, fromRef, toRef, options = {}) {
815
+ const repo = await resolveGitRepository(repoPath, options);
816
+ const { config } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
817
+ const policy = resolveChangeImpactPolicy(config);
818
+ const diff = await runGit(repoPath, ["diff", "--name-only", `${fromRef}..${toRef}`], options);
819
+ const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
820
+ return classifyChangedFileList(files, policy);
821
+ }
811
822
  function resolveChangeImpactConfigForRepo(repoRoot, options) {
812
823
  if (options.changeImpactConfig === null) {
813
824
  return { config: null, sourceKey: "forced-default" };
@@ -6158,6 +6169,7 @@ function reclaimStrandedAssignedTask(meshId, taskId, opts) {
6158
6169
  delete entry.assignedSessionId;
6159
6170
  delete entry.assignedProviderType;
6160
6171
  delete entry.dispatchTimestamp;
6172
+ entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
6161
6173
  entry.strandedReclaimCount = reclaims;
6162
6174
  entry.updatedAt = now;
6163
6175
  endTaskDispatchInFlight(meshId, taskId);
@@ -7181,6 +7193,7 @@ var init_mesh_runtime_store = __esm({
7181
7193
  entry.assignedSessionId = sessionId;
7182
7194
  if (providerType) entry.assignedProviderType = providerType;
7183
7195
  entry.dispatchTimestamp = now;
7196
+ entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
7184
7197
  entry.updatedAt = now;
7185
7198
  this.db.prepare(`
7186
7199
  UPDATE mesh_queue SET
@@ -15649,6 +15662,10 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
15649
15662
  meshId,
15650
15663
  nodeId,
15651
15664
  taskId: task.id,
15665
+ // REDRIVE-DUP: carry the current dispatch nonce so the worker can echo it
15666
+ // back on generating_started; a reclaim bumps this row's nonce, making an
15667
+ // already-in-flight stale inject rejectable on arrival.
15668
+ ...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
15652
15669
  ...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
15653
15670
  ...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
15654
15671
  }
@@ -15707,6 +15724,8 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
15707
15724
  meshId,
15708
15725
  nodeId,
15709
15726
  taskId: task.id,
15727
+ // REDRIVE-DUP: carry the current dispatch nonce (see remote branch above).
15728
+ ...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
15710
15729
  ...localCoordinatorDaemonId() ? { coordinatorDaemonId: localCoordinatorDaemonId() } : {},
15711
15730
  ...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { coordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {}
15712
15731
  }
@@ -19689,6 +19708,42 @@ function sourceWorkerAutoApproves(components, sessionId) {
19689
19708
  return false;
19690
19709
  }
19691
19710
  }
19711
+ function stopStaleMeshWorker(components, args) {
19712
+ const { meshId, sessionId, providerType } = args;
19713
+ const stopArgs = {
19714
+ targetSessionId: sessionId,
19715
+ ...providerType ? { cliType: providerType } : {},
19716
+ mode: "hard",
19717
+ reason: "stale_mesh_dispatch_reclaimed"
19718
+ };
19719
+ try {
19720
+ const isLocal = components.cliManager?.adapters?.has?.(sessionId) === true;
19721
+ if (isLocal) {
19722
+ if (!stopArgs.cliType) {
19723
+ const localType = components.cliManager?.adapters?.get?.(sessionId)?.cliType;
19724
+ if (localType) stopArgs.cliType = localType;
19725
+ }
19726
+ Promise.resolve(components.cliManager?.handleCliCommand?.("stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Local stop of stale worker ${sessionId} failed: ${e?.message || e}`));
19727
+ return;
19728
+ }
19729
+ let daemonId = args.daemonId;
19730
+ if (!daemonId && args.nodeId) {
19731
+ try {
19732
+ const mesh = getMeshWithCache(components, meshId);
19733
+ const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, args.nodeId));
19734
+ daemonId = node ? readMeshNodeDaemonId(node) || void 0 : void 0;
19735
+ } catch {
19736
+ }
19737
+ }
19738
+ if (daemonId && components.dispatchMeshCommand) {
19739
+ Promise.resolve(components.dispatchMeshCommand(daemonId, "stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Remote stop of stale worker ${sessionId} on daemon ${daemonId} failed: ${e?.message || e}`));
19740
+ } else {
19741
+ LOG.warn("MeshQueue", `Cannot stop stale worker ${sessionId}: no local adapter and no resolvable remote daemon id (node ${args.nodeId ?? "?"}). Ack already rejected \u2014 task will re-strand-and-fail if the worker completes.`);
19742
+ }
19743
+ } catch (e) {
19744
+ LOG.warn("MeshQueue", `stopStaleMeshWorker error for ${sessionId}: ${e?.message || e}`);
19745
+ }
19746
+ }
19692
19747
  function injectMeshSystemMessage(components, args) {
19693
19748
  const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
19694
19749
  const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
@@ -19928,6 +19983,35 @@ function injectMeshSystemMessage(components, args) {
19928
19983
  }
19929
19984
  if (sessionId) {
19930
19985
  const startedTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
19986
+ const startedNonce = typeof args.metadataEvent.dispatchNonce === "number" ? args.metadataEvent.dispatchNonce : void 0;
19987
+ if (startedTaskId && startedNonce !== void 0) {
19988
+ const currentRow = (() => {
19989
+ try {
19990
+ return MeshRuntimeStore.getInstance().findQueueEntryById(args.meshId, startedTaskId);
19991
+ } catch {
19992
+ return null;
19993
+ }
19994
+ })();
19995
+ const currentNonce = typeof currentRow?.dispatchNonce === "number" ? currentRow.dispatchNonce : void 0;
19996
+ if (currentNonce !== void 0 && startedNonce < currentNonce) {
19997
+ LOG.warn("MeshQueue", `Rejecting stale mesh dispatch: task ${startedTaskId} generating_started from session ${sessionId} (node ${nodeId ?? "?"}) carries dispatchNonce ${startedNonce} < current ${currentNonce} \u2014 the task was reclaimed and re-dispatched; stopping this worker to prevent duplicate execution.`);
19998
+ traceMeshEventDrop("stale_dispatch_nonce_rejected", {
19999
+ taskId: startedTaskId,
20000
+ sessionId,
20001
+ nodeId,
20002
+ meshId: args.meshId,
20003
+ event: "agent:generating_started"
20004
+ }, `nonce ${startedNonce} < ${currentNonce}`);
20005
+ stopStaleMeshWorker(components, {
20006
+ meshId: args.meshId,
20007
+ sessionId,
20008
+ nodeId,
20009
+ providerType: readNonEmptyString2(args.metadataEvent.providerType) || readNonEmptyString2(args.metadataEvent.cliType),
20010
+ daemonId: readNonEmptyString2(args.metadataEvent.sourceDaemonId) || readNonEmptyString2(args.metadataEvent.daemonId)
20011
+ });
20012
+ return { success: true, forwarded: 0, suppressed: true, staleDispatchRejected: true };
20013
+ }
20014
+ }
19931
20015
  if (startedTaskId) {
19932
20016
  updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
19933
20017
  } else if (sessionHasActiveAssignment(args.meshId, sessionId)) {
@@ -20464,6 +20548,7 @@ var init_mesh_event_forwarding = __esm({
20464
20548
  init_dist();
20465
20549
  init_mesh_events_stale();
20466
20550
  init_mesh_task_inflight();
20551
+ init_mesh_node_identity();
20467
20552
  init_mesh_events_utils();
20468
20553
  init_mesh_event_classify();
20469
20554
  init_mesh_queue_assignment();
@@ -45546,6 +45631,10 @@ var CliProviderInstance = class _CliProviderInstance {
45546
45631
  // shares this daemon. See isMeshOwnedDelegateSession's post-detach gate.
45547
45632
  ...assignment.nodeId ? { meshNodeId: assignment.nodeId, meshLastNodeId: assignment.nodeId } : {},
45548
45633
  ...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
45634
+ // REDRIVE-DUP: task-level dispatch nonce, echoed on generating_started so the
45635
+ // coordinator can reject a stale (reclaimed) dispatch. Cleared with meshActiveTaskId
45636
+ // on detach so a subsequent unrelated turn never re-echoes a prior task's nonce.
45637
+ ...typeof assignment.dispatchNonce === "number" ? { meshActiveDispatchNonce: assignment.dispatchNonce } : {},
45549
45638
  ...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {},
45550
45639
  // Session-level routing anchor: the originating coordinator session, so this
45551
45640
  // worker's completion events route back to the exact session that dispatched it.
@@ -45581,15 +45670,17 @@ var CliProviderInstance = class _CliProviderInstance {
45581
45670
  if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
45582
45671
  if (this.settings.launchedByCoordinator === true) {
45583
45672
  if (!this.settings.meshActiveTaskId) return;
45584
- const { meshActiveTaskId: meshActiveTaskId2, ...rest2 } = this.settings;
45673
+ const { meshActiveTaskId: meshActiveTaskId2, meshActiveDispatchNonce: meshActiveDispatchNonce2, ...rest2 } = this.settings;
45585
45674
  void meshActiveTaskId2;
45675
+ void meshActiveDispatchNonce2;
45586
45676
  this.settings = rest2;
45587
45677
  this.adapter.updateRuntimeSettings?.(this.settings);
45588
45678
  return;
45589
45679
  }
45590
- const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
45680
+ const { meshNodeFor, meshNodeId, meshActiveTaskId, meshActiveDispatchNonce, ...rest } = this.settings;
45591
45681
  void meshNodeFor;
45592
45682
  void meshActiveTaskId;
45683
+ void meshActiveDispatchNonce;
45593
45684
  const lastNodeId = typeof meshNodeId === "string" && meshNodeId.trim() ? meshNodeId.trim() : typeof rest.meshLastNodeId === "string" && rest.meshLastNodeId.trim() ? rest.meshLastNodeId.trim() : void 0;
45594
45685
  this.settings = lastNodeId ? { ...rest, meshLastNodeId: lastNodeId } : rest;
45595
45686
  this.adapter.updateRuntimeSettings?.(this.settings);
@@ -47078,6 +47169,9 @@ var CliProviderInstance = class _CliProviderInstance {
47078
47169
  const resolved = this.completingTurnTaskId();
47079
47170
  if (resolved) enrichedEvent.taskId = resolved;
47080
47171
  }
47172
+ if (enrichedEvent.dispatchNonce === void 0 && typeof this.settings.meshActiveDispatchNonce === "number") {
47173
+ enrichedEvent.dispatchNonce = this.settings.meshActiveDispatchNonce;
47174
+ }
47081
47175
  }
47082
47176
  if (this.context?.emitProviderEvent) {
47083
47177
  this.context.emitProviderEvent(enrichedEvent);
@@ -49969,6 +50063,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
49969
50063
  meshId: meshContext.meshId,
49970
50064
  ...typeof meshContext.nodeId === "string" && meshContext.nodeId ? { nodeId: meshContext.nodeId } : {},
49971
50065
  ...typeof meshContext.taskId === "string" && meshContext.taskId ? { taskId: meshContext.taskId } : {},
50066
+ // REDRIVE-DUP: carry the dispatch nonce onto the worker session so
50067
+ // its generating_started event echoes it back for the coordinator's
50068
+ // stale-nonce guard.
50069
+ ...typeof meshContext.dispatchNonce === "number" ? { dispatchNonce: meshContext.dispatchNonce } : {},
49972
50070
  ...typeof meshContext.coordinatorDaemonId === "string" && meshContext.coordinatorDaemonId ? { coordinatorDaemonId: meshContext.coordinatorDaemonId } : {}
49973
50071
  });
49974
50072
  } catch {
@@ -57804,6 +57902,7 @@ function orderMeshRefineBatchNodes(changeAreas) {
57804
57902
 
57805
57903
  // src/commands/router-refine.ts
57806
57904
  init_repo_mesh_types();
57905
+ init_git_status();
57807
57906
  init_mesh_node_identity();
57808
57907
 
57809
57908
  // src/mesh/mesh-refine-gates.ts
@@ -58744,6 +58843,41 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
58744
58843
  if (fs31.existsSync(pathJoin2(cwd, "node_modules"))) return false;
58745
58844
  return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs31.existsSync(pathJoin2(cwd, lock)));
58746
58845
  };
58846
+ const needsNodeModules = (candidate, cwd) => isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd);
58847
+ const isDaemonScopedCommand = (candidate) => {
58848
+ const haystack = [candidate.command, ...candidate.args || [], candidate.displayCommand || ""].join(" ").toLowerCase();
58849
+ if (candidate.category === "typecheck") return false;
58850
+ if (/\btypecheck\b/.test(haystack)) return false;
58851
+ if (/\bweb-core\b|\bweb-cloud\b|\bweb-standalone\b|\btest:web\b/.test(haystack)) return false;
58852
+ return /\bdaemon-core\b|\bdaemon-cloud\b|\btest:daemon\b|check-vendor-drift/.test(haystack);
58853
+ };
58854
+ const scopeUnaffectedDaemon = opts?.changeImpact?.isDaemonAffecting === false;
58855
+ const skippedDaemonCommands = [];
58856
+ const commandsToRun = [];
58857
+ for (const candidate of selection.commands) {
58858
+ if (scopeUnaffectedDaemon && isDaemonScopedCommand(candidate)) {
58859
+ skippedDaemonCommands.push(candidate.displayCommand);
58860
+ summary.commandsRun.push({
58861
+ command: candidate.command,
58862
+ args: candidate.args,
58863
+ displayCommand: candidate.displayCommand,
58864
+ category: candidate.category,
58865
+ source: candidate.source,
58866
+ passed: true,
58867
+ skipped: true,
58868
+ skipReason: "unaffected_daemon_scope"
58869
+ });
58870
+ continue;
58871
+ }
58872
+ commandsToRun.push(candidate);
58873
+ }
58874
+ if (opts?.changeImpact) {
58875
+ summary.changeImpact = {
58876
+ isDaemonAffecting: opts.changeImpact.isDaemonAffecting,
58877
+ affectedPackages: opts.changeImpact.affectedPackages,
58878
+ ...skippedDaemonCommands.length ? { skippedDaemonCommands } : {}
58879
+ };
58880
+ }
58747
58881
  if (runLegacyBootstrapCommands) {
58748
58882
  summary.bootstrap = { stage: "legacy" };
58749
58883
  for (const candidate of selection.bootstrapCommands) {
@@ -58778,23 +58912,22 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
58778
58912
  }
58779
58913
  }
58780
58914
  }
58781
- for (const candidate of selection.commands) {
58915
+ let missingDepsBlocked = false;
58916
+ for (const candidate of commandsToRun) {
58782
58917
  const startedAt = Date.now();
58783
58918
  const cwd = candidate.cwd ? pathResolve2(workspace, candidate.cwd) : workspace;
58784
58919
  const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
58785
58920
  const bootstrapProvidedDependencies = summary.bootstrap?.stage === "cached" || summary.bootstrap?.stage === "ran" || summary.bootstrap?.stage === "legacy";
58786
- if (!bootstrapProvidedDependencies && isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd)) {
58921
+ if (!bootstrapProvidedDependencies && needsNodeModules(candidate, cwd)) {
58787
58922
  summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, {
58788
- stderr: "Dependencies appear to be missing: package.json and a lockfile are present, but node_modules is absent. Configure validation.bootstrapCommands in repo mesh/refine config if Refinery should install/bootstrap before validation."
58923
+ stderr: "Dependencies appear to be missing: package.json and a lockfile are present, but node_modules is absent. Configure validation.bootstrapCommands (or .adhdev/worktree_bootstrap.json) in repo mesh/refine config if Refinery should install/bootstrap before validation."
58789
58924
  }, false, {
58790
58925
  exitCode: null,
58791
58926
  skipped: true,
58792
58927
  failureKind: "missing_dependencies"
58793
58928
  }));
58794
- summary.status = "failed";
58795
- summary.failureKind = "missing_dependencies";
58796
- summary.failureCode = "missing_dependencies";
58797
- return summary;
58929
+ missingDepsBlocked = true;
58930
+ continue;
58798
58931
  }
58799
58932
  const resolvedCommand = resolveWin32Executable(candidate.command);
58800
58933
  const spawn5 = buildWin32ExecFileSpawn(resolvedCommand, candidate.args);
@@ -58830,6 +58963,12 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
58830
58963
  return summary;
58831
58964
  }
58832
58965
  }
58966
+ if (missingDepsBlocked) {
58967
+ summary.status = "failed";
58968
+ summary.failureKind = "missing_dependencies";
58969
+ summary.failureCode = "missing_dependencies";
58970
+ return summary;
58971
+ }
58833
58972
  summary.status = "passed";
58834
58973
  return summary;
58835
58974
  }
@@ -58862,7 +59001,50 @@ function buildRefineJobHandle(self, args) {
58862
59001
  }
58863
59002
  };
58864
59003
  }
59004
+ function slimRefineEventResult(result) {
59005
+ const slim = {};
59006
+ for (const key2 of [
59007
+ "success",
59008
+ "code",
59009
+ "error",
59010
+ "convergenceStatus",
59011
+ "blockedReason",
59012
+ "branch",
59013
+ "into",
59014
+ "terminalKind",
59015
+ "nextStep",
59016
+ "finalBranchConvergenceState"
59017
+ ]) {
59018
+ if (result[key2] !== void 0) slim[key2] = result[key2];
59019
+ }
59020
+ if (Array.isArray(result.unreachableSubmoduleCommits)) {
59021
+ slim.unreachableSubmoduleCommits = result.unreachableSubmoduleCommits.map((e) => ({ path: e?.path, autoPublishAllowed: e?.autoPublishAllowed }));
59022
+ }
59023
+ if (result.validationSummary && typeof result.validationSummary === "object") {
59024
+ const vs = result.validationSummary;
59025
+ slim.validationSummary = {
59026
+ status: vs.status,
59027
+ failureCode: vs.failureCode,
59028
+ configSource: vs.configSource,
59029
+ configSourceType: vs.configSourceType,
59030
+ commandsRunCount: Array.isArray(vs.commandsRun) ? vs.commandsRun.length : void 0
59031
+ };
59032
+ }
59033
+ if (result.patchEquivalence && typeof result.patchEquivalence === "object") {
59034
+ const pe = result.patchEquivalence;
59035
+ slim.patchEquivalence = { status: pe.status, equivalent: pe.equivalent };
59036
+ }
59037
+ if (result.submoduleReachability && typeof result.submoduleReachability === "object") {
59038
+ const sr = result.submoduleReachability;
59039
+ slim.submoduleReachability = {
59040
+ checked: Array.isArray(sr.entries) ? sr.entries.length : void 0,
59041
+ unreachable: Array.isArray(sr.unreachable) ? sr.unreachable.length : void 0
59042
+ };
59043
+ }
59044
+ return slim;
59045
+ }
58865
59046
  function queueRefineJobEvent(self, event, handle, result) {
59047
+ const slimResult = result ? slimRefineEventResult(result) : void 0;
58866
59048
  const metadataEvent = {
58867
59049
  source: "refine_mesh_node_async_job",
58868
59050
  jobId: handle.jobId,
@@ -58875,7 +59057,7 @@ function queueRefineJobEvent(self, event, handle, result) {
58875
59057
  startedAt: handle.startedAt,
58876
59058
  completedAt: handle.completedAt,
58877
59059
  retryOfJobId: handle.retryOfJobId,
58878
- ...result ? { result } : {}
59060
+ ...slimResult ? { result: slimResult } : {}
58879
59061
  };
58880
59062
  const eventPayload = {
58881
59063
  event,
@@ -58902,7 +59084,7 @@ function queueRefineJobEvent(self, event, handle, result) {
58902
59084
  startedAt: handle.startedAt,
58903
59085
  completedAt: handle.completedAt,
58904
59086
  retryOfJobId: handle.retryOfJobId,
58905
- ...result ? { result } : {}
59087
+ ...slimResult ? { result: slimResult } : {}
58906
59088
  }
58907
59089
  );
58908
59090
  if (forwarded?.success === true) return;
@@ -59037,7 +59219,20 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
59037
59219
  const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
59038
59220
  const baseHead = baseHeadRaw;
59039
59221
  const branchHead = branchHeadStdout.trim();
59040
- recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, { branch, baseBranch, baseHead, branchHead, ...fetchWarning ? { fetchWarning } : {} });
59222
+ let changeImpact;
59223
+ try {
59224
+ changeImpact = await classifyChangedPackages(node.workspace, baseHead, branchHead);
59225
+ } catch {
59226
+ changeImpact = void 0;
59227
+ }
59228
+ recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, {
59229
+ branch,
59230
+ baseBranch,
59231
+ baseHead,
59232
+ branchHead,
59233
+ ...changeImpact ? { changeImpact } : {},
59234
+ ...fetchWarning ? { fetchWarning } : {}
59235
+ });
59041
59236
  return {
59042
59237
  kind: "continue",
59043
59238
  ctx: {
@@ -59054,6 +59249,7 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
59054
59249
  baseBranch,
59055
59250
  baseHead,
59056
59251
  branchHead,
59252
+ changeImpact,
59057
59253
  validationSummary: void 0,
59058
59254
  patchEquivalence: void 0,
59059
59255
  submoduleReachability: void 0
@@ -59064,6 +59260,9 @@ async function refineValidationStage(self, ctx) {
59064
59260
  const { mesh, node, branch, baseBranch, refineStages } = ctx;
59065
59261
  const validationStarted = Date.now();
59066
59262
  const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace, {
59263
+ // (a) Scope the validation command set by coarse change-impact (resolved
59264
+ // in resolve_refs). Undefined → gate runs the full command set (fail-open).
59265
+ changeImpact: ctx.changeImpact,
59067
59266
  // M2-2: consume the node's persisted bootstrap state; persist re-runs.
59068
59267
  persistedBootstrapState: node.worktreeBootstrap,
59069
59268
  onBootstrapStateChange: (state) => {
@@ -59083,7 +59282,7 @@ async function refineValidationStage(self, ctx) {
59083
59282
  if (validationSummary.status === "failed") {
59084
59283
  const firstFailedCmd = Array.isArray(validationSummary.commandsRun) ? validationSummary.commandsRun.find((c) => c.success === false) : void 0;
59085
59284
  const buildValidationFailedError = () => {
59086
- const base = validationSummary.failureCode === "missing_dependencies" ? "Refinery validation dependencies are missing; merge/refine was not attempted. Configure validation.bootstrapCommands if Refinery should bootstrap dependencies before validation." : validationSummary.failureCode === "dependency_bootstrap_failed" ? "Refinery dependency/bootstrap command failed; merge/refine was not attempted." : validationSummary.failureCode === "spawn_resolution_failed" ? validationSummary.spawnResolutionError || "Refinery validation command could not be spawned (executable not found); merge/refine was not attempted." : "Refinery validation gate failed; merge/refine was not attempted.";
59285
+ const base = validationSummary.failureCode === "missing_dependencies" ? "Refinery validation dependencies are missing for a change-affected package; merge/refine was not attempted. To make this self-service, either (1) configure .adhdev/worktree_bootstrap.json (or validation.bootstrapCommands in .adhdev/refine.json) so Refinery installs deps before validation, or (2) converge the branch via the documented manual fast-forward-only bypass (rebase onto the fetched base, verify strict ancestry, then push ff-only) instead of the refine gate." : validationSummary.failureCode === "dependency_bootstrap_failed" ? "Refinery dependency/bootstrap command failed; merge/refine was not attempted." : validationSummary.failureCode === "spawn_resolution_failed" ? validationSummary.spawnResolutionError || "Refinery validation command could not be spawned (executable not found); merge/refine was not attempted." : "Refinery validation gate failed; merge/refine was not attempted.";
59087
59286
  if (!firstFailedCmd) return base;
59088
59287
  const cmdName = typeof firstFailedCmd.displayCommand === "string" ? firstFailedCmd.displayCommand : typeof firstFailedCmd.command === "string" ? [firstFailedCmd.command, ...Array.isArray(firstFailedCmd.args) ? firstFailedCmd.args : []].join(" ").trim() : typeof firstFailedCmd.cmd === "string" ? firstFailedCmd.cmd : "";
59089
59288
  const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");