@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/commands/router-refine.d.ts +14 -0
- package/dist/git/git-status.d.ts +23 -0
- package/dist/index.js +232 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +232 -33
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-event-forwarding.d.ts +9 -0
- package/dist/mesh/mesh-refine-gates.d.ts +29 -0
- package/dist/mesh/mesh-work-queue.d.ts +11 -0
- package/dist/providers/cli-provider-instance.d.ts +1 -0
- package/dist/providers/provider-instance-manager.d.ts +1 -0
- package/dist/providers/provider-instance.d.ts +1 -0
- package/package.json +3 -3
- package/src/commands/cli-manager.ts +4 -0
- package/src/commands/router-refine.ts +87 -4
- package/src/git/git-status.ts +84 -29
- package/src/mesh/mesh-event-forwarding.ts +92 -0
- package/src/mesh/mesh-queue-assignment.ts +6 -0
- package/src/mesh/mesh-refine-gates.ts +113 -7
- package/src/mesh/mesh-runtime-store.ts +5 -0
- package/src/mesh/mesh-work-queue.ts +18 -0
- package/src/providers/cli-provider-instance.ts +17 -6
- package/src/providers/provider-instance-manager.ts +1 -1
- package/src/providers/provider-instance.ts +1 -1
|
@@ -13,6 +13,20 @@ export declare function buildRefineJobHandle(self: DaemonCommandRouter, args: {
|
|
|
13
13
|
retryOfJobId?: string;
|
|
14
14
|
coordinatorDaemonId?: string;
|
|
15
15
|
}): MeshRefineJobHandle;
|
|
16
|
+
/**
|
|
17
|
+
* Slim the terminal-stage refine result down to the fields a coordinator needs to
|
|
18
|
+
* decide next-step, dropping the heavy per-command / per-entry detail.
|
|
19
|
+
*
|
|
20
|
+
* The full `CommandRouterResult` (with `validationSummary.commandsRun[]` carrying
|
|
21
|
+
* per-command stdout/stderr, `rejectedCommands`, `suggestions`, `suggestedConfig`,
|
|
22
|
+
* the full `patchEquivalence`, and `submoduleReachability.entries[]`/`.unreachable[]`)
|
|
23
|
+
* routinely exceeds 70KB and overflows the coordinator token limit when it rides on a
|
|
24
|
+
* `mesh_wait_events` payload. The full detail is still persisted verbatim to the ledger
|
|
25
|
+
* (`appendRefineJobLedger`) and `terminalRefineJobs`, so slimming only the EVENT loses
|
|
26
|
+
* nothing — the coordinator can pull the full record on demand via
|
|
27
|
+
* `evidence.ledgerCommand` / `taskHistoryKind`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function slimRefineEventResult(result: Record<string, unknown>): Record<string, unknown>;
|
|
16
30
|
export declare function queueRefineJobEvent(self: DaemonCommandRouter, event: 'refine:accepted' | 'refine:completed' | 'refine:failed', handle: MeshRefineJobHandle, result?: Record<string, unknown>): void;
|
|
17
31
|
export declare function appendRefineJobLedger(self: DaemonCommandRouter, kind: 'task_dispatched' | 'task_completed' | 'task_failed', handle: MeshRefineJobHandle, result?: Record<string, unknown>): Promise<void>;
|
|
18
32
|
/**
|
package/dist/git/git-status.d.ts
CHANGED
|
@@ -67,6 +67,29 @@ export interface GitStatusOptions {
|
|
|
67
67
|
*/
|
|
68
68
|
export declare const GIT_FETCH_THROTTLE_MS = 30000;
|
|
69
69
|
export declare function getGitRepoStatus(workspace: string, options?: GitStatusOptions): Promise<GitRepoStatus>;
|
|
70
|
+
/** Coarse change-impact verdict produced from a changed-file list. */
|
|
71
|
+
export interface ChangedPackageClassification {
|
|
72
|
+
isDaemonAffecting: boolean;
|
|
73
|
+
affectedPackages: string[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Ref-parameterized change-impact classification for a repo/worktree, reusing the
|
|
77
|
+
* exact daemon-vs-web bucketing that the stale-build detector uses — but over an
|
|
78
|
+
* arbitrary `fromRef..toRef` range (e.g. a refine base head → branch head) instead
|
|
79
|
+
* of the live daemon's build commit → HEAD, and WITHOUT any daemonBuildInfo caching.
|
|
80
|
+
*
|
|
81
|
+
* Policy is resolved the same way as getGitRepoStatus: an explicit
|
|
82
|
+
* `options.changeImpactConfig` wins; otherwise the repo's `.adhdev/change-impact.*`
|
|
83
|
+
* is auto-loaded; otherwise the built-in ADHDev default policy applies. The
|
|
84
|
+
* classification uses `git diff --name-only fromRef..toRef`.
|
|
85
|
+
*
|
|
86
|
+
* FAIL-OPEN on error: if the diff can't be collected (bad ref, not a repo), the
|
|
87
|
+
* caller should treat "no verdict" as "run everything" — so we throw rather than
|
|
88
|
+
* returning a misleading benign verdict. Callers wrap this in try/catch and leave
|
|
89
|
+
* changeImpact undefined on failure. Unclassified/new packages still default to
|
|
90
|
+
* isDaemonAffecting:true (never silently skipped).
|
|
91
|
+
*/
|
|
92
|
+
export declare function classifyChangedPackages(repoPath: string, fromRef: string, toRef: string, options?: GitStatusOptions): Promise<ChangedPackageClassification>;
|
|
70
93
|
interface ParsedPorcelainStatus {
|
|
71
94
|
branch: string | null;
|
|
72
95
|
/** Full HEAD object id from `# branch.oid`, or null when detached/unborn. */
|
package/dist/index.js
CHANGED
|
@@ -414,10 +414,10 @@ function readInjected(value) {
|
|
|
414
414
|
}
|
|
415
415
|
function getDaemonBuildInfo() {
|
|
416
416
|
if (cached) return cached;
|
|
417
|
-
const commit = readInjected(true ? "
|
|
418
|
-
const commitShort = readInjected(true ? "
|
|
419
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
420
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
417
|
+
const commit = readInjected(true ? "a659fcbbe9641240de42b95804a16b8852c5b4eb" : void 0) ?? "unknown";
|
|
418
|
+
const commitShort = readInjected(true ? "a659fcbb" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
419
|
+
const version = readInjected(true ? "0.9.82-rc.488" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
420
|
+
const builtAt = readInjected(true ? "2026-07-10T03:46:52.780Z" : void 0);
|
|
421
421
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
422
422
|
return cached;
|
|
423
423
|
}
|
|
@@ -789,30 +789,41 @@ function isNonRuntimeRootFile(file, policy) {
|
|
|
789
789
|
}
|
|
790
790
|
return false;
|
|
791
791
|
}
|
|
792
|
+
function classifyChangedFileList(files, policy) {
|
|
793
|
+
if (files.length === 0) {
|
|
794
|
+
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
795
|
+
}
|
|
796
|
+
const pkgs = /* @__PURE__ */ new Set();
|
|
797
|
+
let sawRuntimeAmbiguousNonPackage = false;
|
|
798
|
+
for (const file of files) {
|
|
799
|
+
const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
|
|
800
|
+
if (!match) {
|
|
801
|
+
if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
pkgs.add(match[1]);
|
|
805
|
+
}
|
|
806
|
+
const affectedPackages = [...pkgs].sort();
|
|
807
|
+
const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
|
|
808
|
+
return { isDaemonAffecting: !allBenign, affectedPackages };
|
|
809
|
+
}
|
|
792
810
|
async function classifyDaemonBuildChange(repoPath, buildCommit, options, policy) {
|
|
793
811
|
try {
|
|
794
812
|
const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
|
|
795
813
|
const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
796
|
-
|
|
797
|
-
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
798
|
-
}
|
|
799
|
-
const pkgs = /* @__PURE__ */ new Set();
|
|
800
|
-
let sawRuntimeAmbiguousNonPackage = false;
|
|
801
|
-
for (const file of files) {
|
|
802
|
-
const match = file.match(/(?:^|\/)packages\/([^/]+)\//);
|
|
803
|
-
if (!match) {
|
|
804
|
-
if (!isNonRuntimeRootFile(file, policy)) sawRuntimeAmbiguousNonPackage = true;
|
|
805
|
-
continue;
|
|
806
|
-
}
|
|
807
|
-
pkgs.add(match[1]);
|
|
808
|
-
}
|
|
809
|
-
const affectedPackages = [...pkgs].sort();
|
|
810
|
-
const allBenign = !sawRuntimeAmbiguousNonPackage && affectedPackages.every((p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p));
|
|
811
|
-
return { isDaemonAffecting: !allBenign, affectedPackages };
|
|
814
|
+
return classifyChangedFileList(files, policy);
|
|
812
815
|
} catch {
|
|
813
816
|
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
814
817
|
}
|
|
815
818
|
}
|
|
819
|
+
async function classifyChangedPackages(repoPath, fromRef, toRef, options = {}) {
|
|
820
|
+
const repo = await resolveGitRepository(repoPath, options);
|
|
821
|
+
const { config } = resolveChangeImpactConfigForRepo(repo.repoRoot, options);
|
|
822
|
+
const policy = resolveChangeImpactPolicy(config);
|
|
823
|
+
const diff = await runGit(repoPath, ["diff", "--name-only", `${fromRef}..${toRef}`], options);
|
|
824
|
+
const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
825
|
+
return classifyChangedFileList(files, policy);
|
|
826
|
+
}
|
|
816
827
|
function resolveChangeImpactConfigForRepo(repoRoot, options) {
|
|
817
828
|
if (options.changeImpactConfig === null) {
|
|
818
829
|
return { config: null, sourceKey: "forced-default" };
|
|
@@ -6164,6 +6175,7 @@ function reclaimStrandedAssignedTask(meshId, taskId, opts) {
|
|
|
6164
6175
|
delete entry.assignedSessionId;
|
|
6165
6176
|
delete entry.assignedProviderType;
|
|
6166
6177
|
delete entry.dispatchTimestamp;
|
|
6178
|
+
entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
|
|
6167
6179
|
entry.strandedReclaimCount = reclaims;
|
|
6168
6180
|
entry.updatedAt = now;
|
|
6169
6181
|
endTaskDispatchInFlight(meshId, taskId);
|
|
@@ -7188,6 +7200,7 @@ var init_mesh_runtime_store = __esm({
|
|
|
7188
7200
|
entry.assignedSessionId = sessionId;
|
|
7189
7201
|
if (providerType) entry.assignedProviderType = providerType;
|
|
7190
7202
|
entry.dispatchTimestamp = now;
|
|
7203
|
+
entry.dispatchNonce = (entry.dispatchNonce || 0) + 1;
|
|
7191
7204
|
entry.updatedAt = now;
|
|
7192
7205
|
this.db.prepare(`
|
|
7193
7206
|
UPDATE mesh_queue SET
|
|
@@ -15646,6 +15659,10 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
15646
15659
|
meshId,
|
|
15647
15660
|
nodeId,
|
|
15648
15661
|
taskId: task.id,
|
|
15662
|
+
// REDRIVE-DUP: carry the current dispatch nonce so the worker can echo it
|
|
15663
|
+
// back on generating_started; a reclaim bumps this row's nonce, making an
|
|
15664
|
+
// already-in-flight stale inject rejectable on arrival.
|
|
15665
|
+
...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
|
|
15649
15666
|
...localDaemonIdForDispatch ? { coordinatorDaemonId: localDaemonIdForDispatch } : {},
|
|
15650
15667
|
...sourceCoordinatorSessionId ? { coordinatorSessionId: sourceCoordinatorSessionId } : {}
|
|
15651
15668
|
}
|
|
@@ -15704,6 +15721,8 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
|
|
|
15704
15721
|
meshId,
|
|
15705
15722
|
nodeId,
|
|
15706
15723
|
taskId: task.id,
|
|
15724
|
+
// REDRIVE-DUP: carry the current dispatch nonce (see remote branch above).
|
|
15725
|
+
...typeof task.dispatchNonce === "number" ? { dispatchNonce: task.dispatchNonce } : {},
|
|
15707
15726
|
...localCoordinatorDaemonId() ? { coordinatorDaemonId: localCoordinatorDaemonId() } : {},
|
|
15708
15727
|
...readNonEmptyString2(task.sourceCoordinatorSessionId) ? { coordinatorSessionId: readNonEmptyString2(task.sourceCoordinatorSessionId) } : {}
|
|
15709
15728
|
}
|
|
@@ -19687,6 +19706,42 @@ function sourceWorkerAutoApproves(components, sessionId) {
|
|
|
19687
19706
|
return false;
|
|
19688
19707
|
}
|
|
19689
19708
|
}
|
|
19709
|
+
function stopStaleMeshWorker(components, args) {
|
|
19710
|
+
const { meshId, sessionId, providerType } = args;
|
|
19711
|
+
const stopArgs = {
|
|
19712
|
+
targetSessionId: sessionId,
|
|
19713
|
+
...providerType ? { cliType: providerType } : {},
|
|
19714
|
+
mode: "hard",
|
|
19715
|
+
reason: "stale_mesh_dispatch_reclaimed"
|
|
19716
|
+
};
|
|
19717
|
+
try {
|
|
19718
|
+
const isLocal = components.cliManager?.adapters?.has?.(sessionId) === true;
|
|
19719
|
+
if (isLocal) {
|
|
19720
|
+
if (!stopArgs.cliType) {
|
|
19721
|
+
const localType = components.cliManager?.adapters?.get?.(sessionId)?.cliType;
|
|
19722
|
+
if (localType) stopArgs.cliType = localType;
|
|
19723
|
+
}
|
|
19724
|
+
Promise.resolve(components.cliManager?.handleCliCommand?.("stop_cli", stopArgs)).catch((e) => LOG.warn("MeshQueue", `Local stop of stale worker ${sessionId} failed: ${e?.message || e}`));
|
|
19725
|
+
return;
|
|
19726
|
+
}
|
|
19727
|
+
let daemonId = args.daemonId;
|
|
19728
|
+
if (!daemonId && args.nodeId) {
|
|
19729
|
+
try {
|
|
19730
|
+
const mesh = getMeshWithCache(components, meshId);
|
|
19731
|
+
const node = mesh?.nodes?.find((n) => meshNodeIdMatches(n, args.nodeId));
|
|
19732
|
+
daemonId = node ? readMeshNodeDaemonId(node) || void 0 : void 0;
|
|
19733
|
+
} catch {
|
|
19734
|
+
}
|
|
19735
|
+
}
|
|
19736
|
+
if (daemonId && components.dispatchMeshCommand) {
|
|
19737
|
+
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}`));
|
|
19738
|
+
} else {
|
|
19739
|
+
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.`);
|
|
19740
|
+
}
|
|
19741
|
+
} catch (e) {
|
|
19742
|
+
LOG.warn("MeshQueue", `stopStaleMeshWorker error for ${sessionId}: ${e?.message || e}`);
|
|
19743
|
+
}
|
|
19744
|
+
}
|
|
19690
19745
|
function injectMeshSystemMessage(components, args) {
|
|
19691
19746
|
const eventSessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
|
|
19692
19747
|
const eventNodeId = readNonEmptyString2(args.nodeId) || readNonEmptyString2(args.metadataEvent.meshNodeId);
|
|
@@ -19926,6 +19981,35 @@ function injectMeshSystemMessage(components, args) {
|
|
|
19926
19981
|
}
|
|
19927
19982
|
if (sessionId) {
|
|
19928
19983
|
const startedTaskId = readNonEmptyString2(args.metadataEvent.taskId) || void 0;
|
|
19984
|
+
const startedNonce = typeof args.metadataEvent.dispatchNonce === "number" ? args.metadataEvent.dispatchNonce : void 0;
|
|
19985
|
+
if (startedTaskId && startedNonce !== void 0) {
|
|
19986
|
+
const currentRow = (() => {
|
|
19987
|
+
try {
|
|
19988
|
+
return MeshRuntimeStore.getInstance().findQueueEntryById(args.meshId, startedTaskId);
|
|
19989
|
+
} catch {
|
|
19990
|
+
return null;
|
|
19991
|
+
}
|
|
19992
|
+
})();
|
|
19993
|
+
const currentNonce = typeof currentRow?.dispatchNonce === "number" ? currentRow.dispatchNonce : void 0;
|
|
19994
|
+
if (currentNonce !== void 0 && startedNonce < currentNonce) {
|
|
19995
|
+
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.`);
|
|
19996
|
+
traceMeshEventDrop("stale_dispatch_nonce_rejected", {
|
|
19997
|
+
taskId: startedTaskId,
|
|
19998
|
+
sessionId,
|
|
19999
|
+
nodeId,
|
|
20000
|
+
meshId: args.meshId,
|
|
20001
|
+
event: "agent:generating_started"
|
|
20002
|
+
}, `nonce ${startedNonce} < ${currentNonce}`);
|
|
20003
|
+
stopStaleMeshWorker(components, {
|
|
20004
|
+
meshId: args.meshId,
|
|
20005
|
+
sessionId,
|
|
20006
|
+
nodeId,
|
|
20007
|
+
providerType: readNonEmptyString2(args.metadataEvent.providerType) || readNonEmptyString2(args.metadataEvent.cliType),
|
|
20008
|
+
daemonId: readNonEmptyString2(args.metadataEvent.sourceDaemonId) || readNonEmptyString2(args.metadataEvent.daemonId)
|
|
20009
|
+
});
|
|
20010
|
+
return { success: true, forwarded: 0, suppressed: true, staleDispatchRejected: true };
|
|
20011
|
+
}
|
|
20012
|
+
}
|
|
19929
20013
|
if (startedTaskId) {
|
|
19930
20014
|
updateDirectDispatchStatus(args.meshId, sessionId, "acked", startedTaskId);
|
|
19931
20015
|
} else if (sessionHasActiveAssignment(args.meshId, sessionId)) {
|
|
@@ -20462,6 +20546,7 @@ var init_mesh_event_forwarding = __esm({
|
|
|
20462
20546
|
init_dist();
|
|
20463
20547
|
init_mesh_events_stale();
|
|
20464
20548
|
init_mesh_task_inflight();
|
|
20549
|
+
init_mesh_node_identity();
|
|
20465
20550
|
init_mesh_events_utils();
|
|
20466
20551
|
init_mesh_event_classify();
|
|
20467
20552
|
init_mesh_queue_assignment();
|
|
@@ -45966,6 +46051,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
45966
46051
|
// shares this daemon. See isMeshOwnedDelegateSession's post-detach gate.
|
|
45967
46052
|
...assignment.nodeId ? { meshNodeId: assignment.nodeId, meshLastNodeId: assignment.nodeId } : {},
|
|
45968
46053
|
...assignment.taskId ? { meshActiveTaskId: assignment.taskId } : {},
|
|
46054
|
+
// REDRIVE-DUP: task-level dispatch nonce, echoed on generating_started so the
|
|
46055
|
+
// coordinator can reject a stale (reclaimed) dispatch. Cleared with meshActiveTaskId
|
|
46056
|
+
// on detach so a subsequent unrelated turn never re-echoes a prior task's nonce.
|
|
46057
|
+
...typeof assignment.dispatchNonce === "number" ? { meshActiveDispatchNonce: assignment.dispatchNonce } : {},
|
|
45969
46058
|
...assignment.coordinatorDaemonId ? { meshCoordinatorDaemonId: assignment.coordinatorDaemonId } : {},
|
|
45970
46059
|
// Session-level routing anchor: the originating coordinator session, so this
|
|
45971
46060
|
// worker's completion events route back to the exact session that dispatched it.
|
|
@@ -46001,15 +46090,17 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
46001
46090
|
if (!this.settings.meshNodeFor && !this.settings.meshActiveTaskId && !this.settings.meshNodeId) return;
|
|
46002
46091
|
if (this.settings.launchedByCoordinator === true) {
|
|
46003
46092
|
if (!this.settings.meshActiveTaskId) return;
|
|
46004
|
-
const { meshActiveTaskId: meshActiveTaskId2, ...rest2 } = this.settings;
|
|
46093
|
+
const { meshActiveTaskId: meshActiveTaskId2, meshActiveDispatchNonce: meshActiveDispatchNonce2, ...rest2 } = this.settings;
|
|
46005
46094
|
void meshActiveTaskId2;
|
|
46095
|
+
void meshActiveDispatchNonce2;
|
|
46006
46096
|
this.settings = rest2;
|
|
46007
46097
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
46008
46098
|
return;
|
|
46009
46099
|
}
|
|
46010
|
-
const { meshNodeFor, meshNodeId, meshActiveTaskId, ...rest } = this.settings;
|
|
46100
|
+
const { meshNodeFor, meshNodeId, meshActiveTaskId, meshActiveDispatchNonce, ...rest } = this.settings;
|
|
46011
46101
|
void meshNodeFor;
|
|
46012
46102
|
void meshActiveTaskId;
|
|
46103
|
+
void meshActiveDispatchNonce;
|
|
46013
46104
|
const lastNodeId = typeof meshNodeId === "string" && meshNodeId.trim() ? meshNodeId.trim() : typeof rest.meshLastNodeId === "string" && rest.meshLastNodeId.trim() ? rest.meshLastNodeId.trim() : void 0;
|
|
46014
46105
|
this.settings = lastNodeId ? { ...rest, meshLastNodeId: lastNodeId } : rest;
|
|
46015
46106
|
this.adapter.updateRuntimeSettings?.(this.settings);
|
|
@@ -47498,6 +47589,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
47498
47589
|
const resolved = this.completingTurnTaskId();
|
|
47499
47590
|
if (resolved) enrichedEvent.taskId = resolved;
|
|
47500
47591
|
}
|
|
47592
|
+
if (enrichedEvent.dispatchNonce === void 0 && typeof this.settings.meshActiveDispatchNonce === "number") {
|
|
47593
|
+
enrichedEvent.dispatchNonce = this.settings.meshActiveDispatchNonce;
|
|
47594
|
+
}
|
|
47501
47595
|
}
|
|
47502
47596
|
if (this.context?.emitProviderEvent) {
|
|
47503
47597
|
this.context.emitProviderEvent(enrichedEvent);
|
|
@@ -50384,6 +50478,10 @@ Run 'adhdev doctor' for detailed diagnostics.`
|
|
|
50384
50478
|
meshId: meshContext.meshId,
|
|
50385
50479
|
...typeof meshContext.nodeId === "string" && meshContext.nodeId ? { nodeId: meshContext.nodeId } : {},
|
|
50386
50480
|
...typeof meshContext.taskId === "string" && meshContext.taskId ? { taskId: meshContext.taskId } : {},
|
|
50481
|
+
// REDRIVE-DUP: carry the dispatch nonce onto the worker session so
|
|
50482
|
+
// its generating_started event echoes it back for the coordinator's
|
|
50483
|
+
// stale-nonce guard.
|
|
50484
|
+
...typeof meshContext.dispatchNonce === "number" ? { dispatchNonce: meshContext.dispatchNonce } : {},
|
|
50387
50485
|
...typeof meshContext.coordinatorDaemonId === "string" && meshContext.coordinatorDaemonId ? { coordinatorDaemonId: meshContext.coordinatorDaemonId } : {}
|
|
50388
50486
|
});
|
|
50389
50487
|
} catch {
|
|
@@ -58219,6 +58317,7 @@ function orderMeshRefineBatchNodes(changeAreas) {
|
|
|
58219
58317
|
|
|
58220
58318
|
// src/commands/router-refine.ts
|
|
58221
58319
|
init_repo_mesh_types();
|
|
58320
|
+
init_git_status();
|
|
58222
58321
|
init_mesh_node_identity();
|
|
58223
58322
|
|
|
58224
58323
|
// src/mesh/mesh-refine-gates.ts
|
|
@@ -59159,6 +59258,41 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
59159
59258
|
if (fs31.existsSync((0, import_path14.join)(cwd, "node_modules"))) return false;
|
|
59160
59259
|
return ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb", "bun.lock"].some((lock) => fs31.existsSync((0, import_path14.join)(cwd, lock)));
|
|
59161
59260
|
};
|
|
59261
|
+
const needsNodeModules = (candidate, cwd) => isPackageManagerValidation(candidate) && dependenciesLikelyMissing(cwd);
|
|
59262
|
+
const isDaemonScopedCommand = (candidate) => {
|
|
59263
|
+
const haystack = [candidate.command, ...candidate.args || [], candidate.displayCommand || ""].join(" ").toLowerCase();
|
|
59264
|
+
if (candidate.category === "typecheck") return false;
|
|
59265
|
+
if (/\btypecheck\b/.test(haystack)) return false;
|
|
59266
|
+
if (/\bweb-core\b|\bweb-cloud\b|\bweb-standalone\b|\btest:web\b/.test(haystack)) return false;
|
|
59267
|
+
return /\bdaemon-core\b|\bdaemon-cloud\b|\btest:daemon\b|check-vendor-drift/.test(haystack);
|
|
59268
|
+
};
|
|
59269
|
+
const scopeUnaffectedDaemon = opts?.changeImpact?.isDaemonAffecting === false;
|
|
59270
|
+
const skippedDaemonCommands = [];
|
|
59271
|
+
const commandsToRun = [];
|
|
59272
|
+
for (const candidate of selection.commands) {
|
|
59273
|
+
if (scopeUnaffectedDaemon && isDaemonScopedCommand(candidate)) {
|
|
59274
|
+
skippedDaemonCommands.push(candidate.displayCommand);
|
|
59275
|
+
summary.commandsRun.push({
|
|
59276
|
+
command: candidate.command,
|
|
59277
|
+
args: candidate.args,
|
|
59278
|
+
displayCommand: candidate.displayCommand,
|
|
59279
|
+
category: candidate.category,
|
|
59280
|
+
source: candidate.source,
|
|
59281
|
+
passed: true,
|
|
59282
|
+
skipped: true,
|
|
59283
|
+
skipReason: "unaffected_daemon_scope"
|
|
59284
|
+
});
|
|
59285
|
+
continue;
|
|
59286
|
+
}
|
|
59287
|
+
commandsToRun.push(candidate);
|
|
59288
|
+
}
|
|
59289
|
+
if (opts?.changeImpact) {
|
|
59290
|
+
summary.changeImpact = {
|
|
59291
|
+
isDaemonAffecting: opts.changeImpact.isDaemonAffecting,
|
|
59292
|
+
affectedPackages: opts.changeImpact.affectedPackages,
|
|
59293
|
+
...skippedDaemonCommands.length ? { skippedDaemonCommands } : {}
|
|
59294
|
+
};
|
|
59295
|
+
}
|
|
59162
59296
|
if (runLegacyBootstrapCommands) {
|
|
59163
59297
|
summary.bootstrap = { stage: "legacy" };
|
|
59164
59298
|
for (const candidate of selection.bootstrapCommands) {
|
|
@@ -59193,23 +59327,22 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
59193
59327
|
}
|
|
59194
59328
|
}
|
|
59195
59329
|
}
|
|
59196
|
-
|
|
59330
|
+
let missingDepsBlocked = false;
|
|
59331
|
+
for (const candidate of commandsToRun) {
|
|
59197
59332
|
const startedAt = Date.now();
|
|
59198
59333
|
const cwd = candidate.cwd ? (0, import_path14.resolve)(workspace, candidate.cwd) : workspace;
|
|
59199
59334
|
const timeout = candidate.timeoutMs || REFINE_VALIDATION_TIMEOUT_MS;
|
|
59200
59335
|
const bootstrapProvidedDependencies = summary.bootstrap?.stage === "cached" || summary.bootstrap?.stage === "ran" || summary.bootstrap?.stage === "legacy";
|
|
59201
|
-
if (!bootstrapProvidedDependencies &&
|
|
59336
|
+
if (!bootstrapProvidedDependencies && needsNodeModules(candidate, cwd)) {
|
|
59202
59337
|
summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, {
|
|
59203
|
-
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."
|
|
59338
|
+
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."
|
|
59204
59339
|
}, false, {
|
|
59205
59340
|
exitCode: null,
|
|
59206
59341
|
skipped: true,
|
|
59207
59342
|
failureKind: "missing_dependencies"
|
|
59208
59343
|
}));
|
|
59209
|
-
|
|
59210
|
-
|
|
59211
|
-
summary.failureCode = "missing_dependencies";
|
|
59212
|
-
return summary;
|
|
59344
|
+
missingDepsBlocked = true;
|
|
59345
|
+
continue;
|
|
59213
59346
|
}
|
|
59214
59347
|
const resolvedCommand = resolveWin32Executable(candidate.command);
|
|
59215
59348
|
const spawn5 = buildWin32ExecFileSpawn(resolvedCommand, candidate.args);
|
|
@@ -59245,6 +59378,12 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
59245
59378
|
return summary;
|
|
59246
59379
|
}
|
|
59247
59380
|
}
|
|
59381
|
+
if (missingDepsBlocked) {
|
|
59382
|
+
summary.status = "failed";
|
|
59383
|
+
summary.failureKind = "missing_dependencies";
|
|
59384
|
+
summary.failureCode = "missing_dependencies";
|
|
59385
|
+
return summary;
|
|
59386
|
+
}
|
|
59248
59387
|
summary.status = "passed";
|
|
59249
59388
|
return summary;
|
|
59250
59389
|
}
|
|
@@ -59277,7 +59416,50 @@ function buildRefineJobHandle(self, args) {
|
|
|
59277
59416
|
}
|
|
59278
59417
|
};
|
|
59279
59418
|
}
|
|
59419
|
+
function slimRefineEventResult(result) {
|
|
59420
|
+
const slim = {};
|
|
59421
|
+
for (const key2 of [
|
|
59422
|
+
"success",
|
|
59423
|
+
"code",
|
|
59424
|
+
"error",
|
|
59425
|
+
"convergenceStatus",
|
|
59426
|
+
"blockedReason",
|
|
59427
|
+
"branch",
|
|
59428
|
+
"into",
|
|
59429
|
+
"terminalKind",
|
|
59430
|
+
"nextStep",
|
|
59431
|
+
"finalBranchConvergenceState"
|
|
59432
|
+
]) {
|
|
59433
|
+
if (result[key2] !== void 0) slim[key2] = result[key2];
|
|
59434
|
+
}
|
|
59435
|
+
if (Array.isArray(result.unreachableSubmoduleCommits)) {
|
|
59436
|
+
slim.unreachableSubmoduleCommits = result.unreachableSubmoduleCommits.map((e) => ({ path: e?.path, autoPublishAllowed: e?.autoPublishAllowed }));
|
|
59437
|
+
}
|
|
59438
|
+
if (result.validationSummary && typeof result.validationSummary === "object") {
|
|
59439
|
+
const vs = result.validationSummary;
|
|
59440
|
+
slim.validationSummary = {
|
|
59441
|
+
status: vs.status,
|
|
59442
|
+
failureCode: vs.failureCode,
|
|
59443
|
+
configSource: vs.configSource,
|
|
59444
|
+
configSourceType: vs.configSourceType,
|
|
59445
|
+
commandsRunCount: Array.isArray(vs.commandsRun) ? vs.commandsRun.length : void 0
|
|
59446
|
+
};
|
|
59447
|
+
}
|
|
59448
|
+
if (result.patchEquivalence && typeof result.patchEquivalence === "object") {
|
|
59449
|
+
const pe = result.patchEquivalence;
|
|
59450
|
+
slim.patchEquivalence = { status: pe.status, equivalent: pe.equivalent };
|
|
59451
|
+
}
|
|
59452
|
+
if (result.submoduleReachability && typeof result.submoduleReachability === "object") {
|
|
59453
|
+
const sr = result.submoduleReachability;
|
|
59454
|
+
slim.submoduleReachability = {
|
|
59455
|
+
checked: Array.isArray(sr.entries) ? sr.entries.length : void 0,
|
|
59456
|
+
unreachable: Array.isArray(sr.unreachable) ? sr.unreachable.length : void 0
|
|
59457
|
+
};
|
|
59458
|
+
}
|
|
59459
|
+
return slim;
|
|
59460
|
+
}
|
|
59280
59461
|
function queueRefineJobEvent(self, event, handle, result) {
|
|
59462
|
+
const slimResult = result ? slimRefineEventResult(result) : void 0;
|
|
59281
59463
|
const metadataEvent = {
|
|
59282
59464
|
source: "refine_mesh_node_async_job",
|
|
59283
59465
|
jobId: handle.jobId,
|
|
@@ -59290,7 +59472,7 @@ function queueRefineJobEvent(self, event, handle, result) {
|
|
|
59290
59472
|
startedAt: handle.startedAt,
|
|
59291
59473
|
completedAt: handle.completedAt,
|
|
59292
59474
|
retryOfJobId: handle.retryOfJobId,
|
|
59293
|
-
...
|
|
59475
|
+
...slimResult ? { result: slimResult } : {}
|
|
59294
59476
|
};
|
|
59295
59477
|
const eventPayload = {
|
|
59296
59478
|
event,
|
|
@@ -59317,7 +59499,7 @@ function queueRefineJobEvent(self, event, handle, result) {
|
|
|
59317
59499
|
startedAt: handle.startedAt,
|
|
59318
59500
|
completedAt: handle.completedAt,
|
|
59319
59501
|
retryOfJobId: handle.retryOfJobId,
|
|
59320
|
-
...
|
|
59502
|
+
...slimResult ? { result: slimResult } : {}
|
|
59321
59503
|
}
|
|
59322
59504
|
);
|
|
59323
59505
|
if (forwarded?.success === true) return;
|
|
@@ -59452,7 +59634,20 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
|
|
|
59452
59634
|
const { stdout: branchHeadStdout } = await execFileAsync4("git", ["rev-parse", branch], { cwd: node.workspace, encoding: "utf8" });
|
|
59453
59635
|
const baseHead = baseHeadRaw;
|
|
59454
59636
|
const branchHead = branchHeadStdout.trim();
|
|
59455
|
-
|
|
59637
|
+
let changeImpact;
|
|
59638
|
+
try {
|
|
59639
|
+
changeImpact = await classifyChangedPackages(node.workspace, baseHead, branchHead);
|
|
59640
|
+
} catch {
|
|
59641
|
+
changeImpact = void 0;
|
|
59642
|
+
}
|
|
59643
|
+
recordMeshRefineStage(refineStages, "resolve_refs", "passed", resolveStarted, {
|
|
59644
|
+
branch,
|
|
59645
|
+
baseBranch,
|
|
59646
|
+
baseHead,
|
|
59647
|
+
branchHead,
|
|
59648
|
+
...changeImpact ? { changeImpact } : {},
|
|
59649
|
+
...fetchWarning ? { fetchWarning } : {}
|
|
59650
|
+
});
|
|
59456
59651
|
return {
|
|
59457
59652
|
kind: "continue",
|
|
59458
59653
|
ctx: {
|
|
@@ -59469,6 +59664,7 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
|
|
|
59469
59664
|
baseBranch,
|
|
59470
59665
|
baseHead,
|
|
59471
59666
|
branchHead,
|
|
59667
|
+
changeImpact,
|
|
59472
59668
|
validationSummary: void 0,
|
|
59473
59669
|
patchEquivalence: void 0,
|
|
59474
59670
|
submoduleReachability: void 0
|
|
@@ -59479,6 +59675,9 @@ async function refineValidationStage(self, ctx) {
|
|
|
59479
59675
|
const { mesh, node, branch, baseBranch, refineStages } = ctx;
|
|
59480
59676
|
const validationStarted = Date.now();
|
|
59481
59677
|
const validationSummary = await runMeshRefineValidationGate(mesh, node.workspace, {
|
|
59678
|
+
// (a) Scope the validation command set by coarse change-impact (resolved
|
|
59679
|
+
// in resolve_refs). Undefined → gate runs the full command set (fail-open).
|
|
59680
|
+
changeImpact: ctx.changeImpact,
|
|
59482
59681
|
// M2-2: consume the node's persisted bootstrap state; persist re-runs.
|
|
59483
59682
|
persistedBootstrapState: node.worktreeBootstrap,
|
|
59484
59683
|
onBootstrapStateChange: (state) => {
|
|
@@ -59498,7 +59697,7 @@ async function refineValidationStage(self, ctx) {
|
|
|
59498
59697
|
if (validationSummary.status === "failed") {
|
|
59499
59698
|
const firstFailedCmd = Array.isArray(validationSummary.commandsRun) ? validationSummary.commandsRun.find((c) => c.success === false) : void 0;
|
|
59500
59699
|
const buildValidationFailedError = () => {
|
|
59501
|
-
const base = validationSummary.failureCode === "missing_dependencies" ? "Refinery validation dependencies are missing; merge/refine was not attempted.
|
|
59700
|
+
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.";
|
|
59502
59701
|
if (!firstFailedCmd) return base;
|
|
59503
59702
|
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 : "";
|
|
59504
59703
|
const rawOutput = [firstFailedCmd.stdout, firstFailedCmd.stderr, firstFailedCmd.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");
|