@adhdev/daemon-core 0.9.82-rc.522 → 0.9.82-rc.524
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 +86 -5
- package/dist/commands/router.d.ts +9 -0
- package/dist/git/git-status.d.ts +14 -1
- package/dist/index.js +641 -159
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +641 -159
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/mesh-queue-assignment.d.ts +13 -0
- package/dist/mesh/mesh-refine-gates.d.ts +5 -0
- package/dist/mesh/refine-config.d.ts +36 -0
- package/package.json +3 -3
- package/src/commands/router-refine.ts +756 -164
- package/src/commands/router.ts +9 -0
- package/src/git/git-status.ts +38 -6
- package/src/mesh/mesh-queue-assignment.ts +64 -2
- package/src/mesh/mesh-reconcile-loop.ts +18 -1
- package/src/mesh/mesh-refine-gates.ts +49 -1
- package/src/mesh/refine-config.ts +44 -0
package/dist/index.js
CHANGED
|
@@ -419,10 +419,10 @@ function readInjected(value) {
|
|
|
419
419
|
}
|
|
420
420
|
function getDaemonBuildInfo() {
|
|
421
421
|
if (cached) return cached;
|
|
422
|
-
const commit = readInjected(true ? "
|
|
423
|
-
const commitShort = readInjected(true ? "
|
|
424
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
425
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
422
|
+
const commit = readInjected(true ? "91ed5e0752d70b62ebcc2cddcea4d502756cff0a" : void 0) ?? "unknown";
|
|
423
|
+
const commitShort = readInjected(true ? "91ed5e07" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
424
|
+
const version = readInjected(true ? "0.9.82-rc.524" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
425
|
+
const builtAt = readInjected(true ? "2026-07-14T07:23:02.573Z" : void 0);
|
|
426
426
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
427
427
|
return cached;
|
|
428
428
|
}
|
|
@@ -794,6 +794,9 @@ function isNonRuntimeRootFile(file, policy) {
|
|
|
794
794
|
}
|
|
795
795
|
return false;
|
|
796
796
|
}
|
|
797
|
+
function deriveChangeArea(isDaemonAffecting, affectedPackages) {
|
|
798
|
+
return isDaemonAffecting ? "daemon" : affectedPackages.length > 0 ? "web" : "none";
|
|
799
|
+
}
|
|
797
800
|
function classifyChangedFileList(files, policy) {
|
|
798
801
|
if (files.length === 0) {
|
|
799
802
|
return { isDaemonAffecting: true, affectedPackages: [], ambiguousNonPackageFiles: [] };
|
|
@@ -816,9 +819,10 @@ async function classifyDaemonBuildChange(repoPath, buildCommit, options, policy)
|
|
|
816
819
|
try {
|
|
817
820
|
const diff = await runGit(repoPath, ["diff", "--name-only", `${buildCommit}..HEAD`], options);
|
|
818
821
|
const files = diff.stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
819
|
-
|
|
822
|
+
const { isDaemonAffecting, affectedPackages } = classifyChangedFileList(files, policy);
|
|
823
|
+
return { isDaemonAffecting, affectedPackages, changeArea: deriveChangeArea(isDaemonAffecting, affectedPackages) };
|
|
820
824
|
} catch {
|
|
821
|
-
return { isDaemonAffecting: true, affectedPackages: [] };
|
|
825
|
+
return { isDaemonAffecting: true, affectedPackages: [], changeArea: "daemon" };
|
|
822
826
|
}
|
|
823
827
|
}
|
|
824
828
|
async function classifyChangedPackages(repoPath, fromRef, toRef, options = {}) {
|
|
@@ -831,7 +835,7 @@ async function classifyChangedPackages(repoPath, fromRef, toRef, options = {}) {
|
|
|
831
835
|
return refineVerdictThroughSubmodules(repoPath, fromRef, toRef, options, policy, rootVerdict);
|
|
832
836
|
}
|
|
833
837
|
async function refineVerdictThroughSubmodules(repoPath, fromRef, toRef, options, policy, rootVerdict) {
|
|
834
|
-
const strip = ({ isDaemonAffecting, affectedPackages }) => ({ isDaemonAffecting, affectedPackages });
|
|
838
|
+
const strip = ({ isDaemonAffecting, affectedPackages: affectedPackages2 }) => ({ isDaemonAffecting, affectedPackages: affectedPackages2, changeArea: deriveChangeArea(isDaemonAffecting, affectedPackages2) });
|
|
835
839
|
const ambiguous = rootVerdict.ambiguousNonPackageFiles;
|
|
836
840
|
if (ambiguous.length === 0) return strip(rootVerdict);
|
|
837
841
|
let submodulePaths;
|
|
@@ -863,9 +867,11 @@ async function refineVerdictThroughSubmodules(repoPath, fromRef, toRef, options,
|
|
|
863
867
|
return strip(rootVerdict);
|
|
864
868
|
}
|
|
865
869
|
if (subVerdict.isDaemonAffecting) {
|
|
870
|
+
const affectedPackages2 = [.../* @__PURE__ */ new Set([...rootVerdict.affectedPackages, ...subVerdict.affectedPackages])].sort();
|
|
866
871
|
return {
|
|
867
872
|
isDaemonAffecting: true,
|
|
868
|
-
affectedPackages:
|
|
873
|
+
affectedPackages: affectedPackages2,
|
|
874
|
+
changeArea: deriveChangeArea(true, affectedPackages2)
|
|
869
875
|
};
|
|
870
876
|
}
|
|
871
877
|
submoduleAffectedPackages.push(...subVerdict.affectedPackages);
|
|
@@ -873,9 +879,11 @@ async function refineVerdictThroughSubmodules(repoPath, fromRef, toRef, options,
|
|
|
873
879
|
const rootPackagesBenign = rootVerdict.affectedPackages.every(
|
|
874
880
|
(p) => policy.webOnlyPackages.has(p) && !policy.daemonRuntimePackages.has(p)
|
|
875
881
|
);
|
|
882
|
+
const affectedPackages = [.../* @__PURE__ */ new Set([...rootVerdict.affectedPackages, ...submoduleAffectedPackages])].sort();
|
|
876
883
|
return {
|
|
877
884
|
isDaemonAffecting: !rootPackagesBenign,
|
|
878
|
-
affectedPackages
|
|
885
|
+
affectedPackages,
|
|
886
|
+
changeArea: deriveChangeArea(!rootPackagesBenign, affectedPackages)
|
|
879
887
|
};
|
|
880
888
|
}
|
|
881
889
|
async function listSubmodulePaths(repoPath, options) {
|
|
@@ -10753,6 +10761,14 @@ function normalizeMeshCommandConfig(entry, source) {
|
|
|
10753
10761
|
if (entry.env !== void 0 && (!isMeshConfigRecord(entry.env) || !Object.values(entry.env).every((value) => typeof value === "string"))) {
|
|
10754
10762
|
return { rejected: { source, command: commandText, reason: "env must be an object of string values" } };
|
|
10755
10763
|
}
|
|
10764
|
+
let scopes;
|
|
10765
|
+
if (entry.scopes !== void 0) {
|
|
10766
|
+
if (!Array.isArray(entry.scopes) || !entry.scopes.every((s2) => MESH_REFINE_VALIDATION_SCOPES.includes(s2))) {
|
|
10767
|
+
return { rejected: { source, command: commandText, reason: `scopes must be an array of ${MESH_REFINE_VALIDATION_SCOPES.join(" | ")}` } };
|
|
10768
|
+
}
|
|
10769
|
+
const deduped = [...new Set(entry.scopes)];
|
|
10770
|
+
scopes = deduped.length ? deduped : void 0;
|
|
10771
|
+
}
|
|
10756
10772
|
return {
|
|
10757
10773
|
command: {
|
|
10758
10774
|
command,
|
|
@@ -10763,7 +10779,8 @@ function normalizeMeshCommandConfig(entry, source) {
|
|
|
10763
10779
|
...typeof entry.cwd === "string" && entry.cwd.trim() ? { cwd: entry.cwd.trim() } : {},
|
|
10764
10780
|
...typeof entry.timeoutMs === "number" ? { timeoutMs: entry.timeoutMs } : {},
|
|
10765
10781
|
...typeof entry.outputLimitBytes === "number" ? { outputLimitBytes: entry.outputLimitBytes } : {},
|
|
10766
|
-
...isMeshConfigRecord(entry.env) ? { env: entry.env } : {}
|
|
10782
|
+
...isMeshConfigRecord(entry.env) ? { env: entry.env } : {},
|
|
10783
|
+
...scopes ? { scopes } : {}
|
|
10767
10784
|
}
|
|
10768
10785
|
};
|
|
10769
10786
|
}
|
|
@@ -10920,7 +10937,7 @@ function resolveMeshRefineValidationPlan(mesh, workspace) {
|
|
|
10920
10937
|
unavailableReason: validation.commands.length ? void 0 : "validation_unavailable: repo mesh/refine config has no validation.commands"
|
|
10921
10938
|
};
|
|
10922
10939
|
}
|
|
10923
|
-
var import_fs8, import_path8, yaml2, MESH_REFINE_VALIDATION_CATEGORIES, MESH_REFINE_CONFIG_LOCATIONS, MESH_REFINE_CONFIG_SCHEMA, SHELL_METACHAR_RE, SAFE_TOKEN_RE, SAFE_WIN32_EXEC_TOKEN_RE, isRecord2;
|
|
10940
|
+
var import_fs8, import_path8, yaml2, MESH_REFINE_VALIDATION_CATEGORIES, MESH_REFINE_VALIDATION_SCOPES, MESH_REFINE_CONFIG_LOCATIONS, MESH_REFINE_CONFIG_SCHEMA, SHELL_METACHAR_RE, SAFE_TOKEN_RE, SAFE_WIN32_EXEC_TOKEN_RE, isRecord2;
|
|
10924
10941
|
var init_refine_config = __esm({
|
|
10925
10942
|
"src/mesh/refine-config.ts"() {
|
|
10926
10943
|
"use strict";
|
|
@@ -10928,6 +10945,7 @@ var init_refine_config = __esm({
|
|
|
10928
10945
|
import_path8 = require("path");
|
|
10929
10946
|
yaml2 = __toESM(require("js-yaml"));
|
|
10930
10947
|
MESH_REFINE_VALIDATION_CATEGORIES = ["typecheck", "test", "lint", "build"];
|
|
10948
|
+
MESH_REFINE_VALIDATION_SCOPES = ["none", "web", "daemon"];
|
|
10931
10949
|
MESH_REFINE_CONFIG_LOCATIONS = [
|
|
10932
10950
|
".adhdev/refine.json",
|
|
10933
10951
|
".adhdev/refine.yaml",
|
|
@@ -10977,7 +10995,12 @@ var init_refine_config = __esm({
|
|
|
10977
10995
|
cwd: { type: "string" },
|
|
10978
10996
|
timeoutMs: { type: "number", minimum: 1e3, maximum: 6e5 },
|
|
10979
10997
|
outputLimitBytes: { type: "number", minimum: 1024, maximum: 1048576 },
|
|
10980
|
-
env: { type: "object", additionalProperties: { type: "string" } }
|
|
10998
|
+
env: { type: "object", additionalProperties: { type: "string" } },
|
|
10999
|
+
scopes: {
|
|
11000
|
+
type: "array",
|
|
11001
|
+
items: { enum: [...MESH_REFINE_VALIDATION_SCOPES] },
|
|
11002
|
+
description: "DOCS-ROOT: change-impact scopes this command runs in ('none'=docs-only, 'web', 'daemon'). Omitted/empty \u2192 runs in every area."
|
|
11003
|
+
}
|
|
10981
11004
|
}
|
|
10982
11005
|
}
|
|
10983
11006
|
},
|
|
@@ -10996,7 +11019,11 @@ var init_refine_config = __esm({
|
|
|
10996
11019
|
cwd: { type: "string" },
|
|
10997
11020
|
timeoutMs: { type: "number", minimum: 1e3, maximum: 6e5 },
|
|
10998
11021
|
outputLimitBytes: { type: "number", minimum: 1024, maximum: 1048576 },
|
|
10999
|
-
env: { type: "object", additionalProperties: { type: "string" } }
|
|
11022
|
+
env: { type: "object", additionalProperties: { type: "string" } },
|
|
11023
|
+
scopes: {
|
|
11024
|
+
type: "array",
|
|
11025
|
+
items: { enum: [...MESH_REFINE_VALIDATION_SCOPES] }
|
|
11026
|
+
}
|
|
11000
11027
|
}
|
|
11001
11028
|
}
|
|
11002
11029
|
}
|
|
@@ -17216,6 +17243,56 @@ async function runContinuousAutoFastForwardScan(components, mesh) {
|
|
|
17216
17243
|
await delegateRemoteAutoFastForward(components, { meshId, nodeId, node, daemonId, workspace, policy, trigger: "reconcile_auto" });
|
|
17217
17244
|
}
|
|
17218
17245
|
}
|
|
17246
|
+
async function runPendingCoordinatorCatchupScan(components, mesh) {
|
|
17247
|
+
const meshId = readNonEmptyString2(mesh?.id);
|
|
17248
|
+
if (!meshId) return;
|
|
17249
|
+
const localIds = expandDaemonIdForms([
|
|
17250
|
+
readNonEmptyString2(components.statusInstanceId),
|
|
17251
|
+
readNonEmptyString2(loadConfig().machineId)
|
|
17252
|
+
]);
|
|
17253
|
+
let markers = [];
|
|
17254
|
+
try {
|
|
17255
|
+
markers = drainPendingMeshCoordinatorEvents(
|
|
17256
|
+
meshId,
|
|
17257
|
+
localIds.length > 0 ? localIds : void 0,
|
|
17258
|
+
{ onlyEvents: /* @__PURE__ */ new Set(["coordinator_catchup"]) }
|
|
17259
|
+
);
|
|
17260
|
+
} catch (e) {
|
|
17261
|
+
LOG.warn("MeshReconcile", `Coordinator-catchup drain failed for mesh ${meshId}: ${e?.message || e}`);
|
|
17262
|
+
return;
|
|
17263
|
+
}
|
|
17264
|
+
if (markers.length === 0) return;
|
|
17265
|
+
const nodes = Array.isArray(mesh?.nodes) ? mesh.nodes : [];
|
|
17266
|
+
for (const marker of markers) {
|
|
17267
|
+
const meta = marker.metadataEvent || {};
|
|
17268
|
+
const nodeId = readNonEmptyString2(marker.nodeId) || readNonEmptyString2(meta.nodeId);
|
|
17269
|
+
const workspace = readNonEmptyString2(marker.workspace) || readNonEmptyString2(meta.workspace);
|
|
17270
|
+
const baseBranch = readNonEmptyString2(meta.baseBranch);
|
|
17271
|
+
if (!workspace) continue;
|
|
17272
|
+
if (nodeId && nodeHasActiveMeshWork(components, meshId, nodeId)) {
|
|
17273
|
+
try {
|
|
17274
|
+
queuePendingMeshCoordinatorEvent(marker);
|
|
17275
|
+
} catch {
|
|
17276
|
+
}
|
|
17277
|
+
continue;
|
|
17278
|
+
}
|
|
17279
|
+
try {
|
|
17280
|
+
const ff = await fastForwardMeshNode({
|
|
17281
|
+
meshId,
|
|
17282
|
+
...nodeId ? { nodeId } : {},
|
|
17283
|
+
workspace,
|
|
17284
|
+
...baseBranch ? { branch: baseBranch } : {},
|
|
17285
|
+
mode: "merge",
|
|
17286
|
+
execute: true,
|
|
17287
|
+
trigger: "refine_post_push_catchup",
|
|
17288
|
+
allowAutoPublishSubmoduleMainCommits: mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true
|
|
17289
|
+
});
|
|
17290
|
+
LOG.info("MeshReconcile", `Coordinator catch-up ff for ${meshId}/${nodeId || workspace}: ${ff.code} (executed=${ff.executed})`);
|
|
17291
|
+
} catch (e) {
|
|
17292
|
+
LOG.warn("MeshReconcile", `Coordinator catch-up ff failed for ${meshId}/${nodeId || workspace}: ${e?.message || e}`);
|
|
17293
|
+
}
|
|
17294
|
+
}
|
|
17295
|
+
}
|
|
17219
17296
|
function runIdleMaintenanceThenAssignQueue(components, args) {
|
|
17220
17297
|
setImmediate(() => {
|
|
17221
17298
|
maybeAutoFastForwardIdleNode(components, args).finally(() => {
|
|
@@ -22212,6 +22289,15 @@ async function runMeshReconcileTick(components) {
|
|
|
22212
22289
|
}
|
|
22213
22290
|
}
|
|
22214
22291
|
}
|
|
22292
|
+
for (const mesh of listMeshes()) {
|
|
22293
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
22294
|
+
if (!daemonHostsMesh(mesh, selfIds)) continue;
|
|
22295
|
+
try {
|
|
22296
|
+
await runPendingCoordinatorCatchupScan(components, mesh);
|
|
22297
|
+
} catch (e) {
|
|
22298
|
+
LOG.warn("MeshReconcile", `Coordinator catch-up scan failed for mesh ${mesh.id}: ${e?.message || e}`);
|
|
22299
|
+
}
|
|
22300
|
+
}
|
|
22215
22301
|
if (dispatchMeshCommand) {
|
|
22216
22302
|
for (const mesh of listMeshes()) {
|
|
22217
22303
|
const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
|
|
@@ -59315,6 +59401,8 @@ init_logger();
|
|
|
59315
59401
|
init_debug_trace();
|
|
59316
59402
|
init_dist();
|
|
59317
59403
|
init_mesh_events();
|
|
59404
|
+
init_mesh_reconcile_identity();
|
|
59405
|
+
init_mesh_fast_forward();
|
|
59318
59406
|
|
|
59319
59407
|
// src/mesh/mesh-refine-batch.ts
|
|
59320
59408
|
var import_node_child_process5 = require("child_process");
|
|
@@ -60244,7 +60332,10 @@ function buildMeshRefineValidationPlan(mesh, workspace) {
|
|
|
60244
60332
|
category: command.category,
|
|
60245
60333
|
source: command.source,
|
|
60246
60334
|
cwd: command.cwd,
|
|
60247
|
-
timeoutMs: command.timeoutMs
|
|
60335
|
+
timeoutMs: command.timeoutMs,
|
|
60336
|
+
// DOCS-ROOT: surface the change-impact scopes so `mesh_refine_config` shows which
|
|
60337
|
+
// area(s) each command runs in (absent → every area).
|
|
60338
|
+
...command.scopes ? { scopes: command.scopes } : {}
|
|
60248
60339
|
});
|
|
60249
60340
|
return {
|
|
60250
60341
|
source: plan.source,
|
|
@@ -60357,9 +60448,33 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
60357
60448
|
return /\bdaemon-core\b|\bdaemon-cloud\b|\btest:daemon\b|check-vendor-drift/.test(haystack);
|
|
60358
60449
|
};
|
|
60359
60450
|
const scopeUnaffectedDaemon = opts?.changeImpact?.isDaemonAffecting === false;
|
|
60451
|
+
const changeArea = opts?.changeImpact?.changeArea;
|
|
60452
|
+
const commandRunsInArea = (candidate) => {
|
|
60453
|
+
if (!changeArea) return true;
|
|
60454
|
+
const scopes = candidate.scopes;
|
|
60455
|
+
if (scopes && scopes.length) return scopes.includes(changeArea);
|
|
60456
|
+
return changeArea !== "none";
|
|
60457
|
+
};
|
|
60360
60458
|
const skippedDaemonCommands = [];
|
|
60459
|
+
const skippedScopeCommands = [];
|
|
60361
60460
|
const commandsToRun = [];
|
|
60362
60461
|
for (const candidate of selection.commands) {
|
|
60462
|
+
if (!commandRunsInArea(candidate)) {
|
|
60463
|
+
skippedScopeCommands.push(candidate.displayCommand);
|
|
60464
|
+
summary.commandsRun.push({
|
|
60465
|
+
command: candidate.command,
|
|
60466
|
+
args: candidate.args,
|
|
60467
|
+
displayCommand: candidate.displayCommand,
|
|
60468
|
+
category: candidate.category,
|
|
60469
|
+
source: candidate.source,
|
|
60470
|
+
passed: true,
|
|
60471
|
+
skipped: true,
|
|
60472
|
+
skipReason: "unaffected_change_scope",
|
|
60473
|
+
changeArea,
|
|
60474
|
+
...candidate.scopes ? { scopes: candidate.scopes } : {}
|
|
60475
|
+
});
|
|
60476
|
+
continue;
|
|
60477
|
+
}
|
|
60363
60478
|
if (scopeUnaffectedDaemon && isDaemonScopedCommand(candidate)) {
|
|
60364
60479
|
skippedDaemonCommands.push(candidate.displayCommand);
|
|
60365
60480
|
summary.commandsRun.push({
|
|
@@ -60380,7 +60495,9 @@ async function runMeshRefineValidationGate(mesh, workspace, opts) {
|
|
|
60380
60495
|
summary.changeImpact = {
|
|
60381
60496
|
isDaemonAffecting: opts.changeImpact.isDaemonAffecting,
|
|
60382
60497
|
affectedPackages: opts.changeImpact.affectedPackages,
|
|
60383
|
-
...
|
|
60498
|
+
...changeArea ? { changeArea } : {},
|
|
60499
|
+
...skippedDaemonCommands.length ? { skippedDaemonCommands } : {},
|
|
60500
|
+
...skippedScopeCommands.length ? { skippedScopeCommands } : {}
|
|
60384
60501
|
};
|
|
60385
60502
|
}
|
|
60386
60503
|
if (runLegacyBootstrapCommands) {
|
|
@@ -60506,6 +60623,24 @@ function buildRefineJobHandle(self, args) {
|
|
|
60506
60623
|
}
|
|
60507
60624
|
};
|
|
60508
60625
|
}
|
|
60626
|
+
function extractValidationFailureDiagnostics(validationSummary) {
|
|
60627
|
+
if (!validationSummary || typeof validationSummary !== "object") return void 0;
|
|
60628
|
+
const commandsRun = Array.isArray(validationSummary.commandsRun) ? validationSummary.commandsRun : [];
|
|
60629
|
+
const failed = commandsRun.find((c) => c.passed === false);
|
|
60630
|
+
const summaryFailureKind = validationSummary.failureKind;
|
|
60631
|
+
if (!failed) {
|
|
60632
|
+
return summaryFailureKind !== void 0 ? { failureKind: summaryFailureKind } : void 0;
|
|
60633
|
+
}
|
|
60634
|
+
const firstFailedCommand = typeof failed.displayCommand === "string" ? failed.displayCommand : typeof failed.command === "string" ? [failed.command, ...Array.isArray(failed.args) ? failed.args : []].join(" ").trim() : void 0;
|
|
60635
|
+
const rawOutput = [failed.stderr, failed.stdout, failed.output].filter((s2) => typeof s2 === "string" && s2.length > 0).join("\n");
|
|
60636
|
+
const outputTail = rawOutput.length > 600 ? rawOutput.slice(-600) : rawOutput;
|
|
60637
|
+
return {
|
|
60638
|
+
...firstFailedCommand ? { firstFailedCommand } : {},
|
|
60639
|
+
...failed.exitCode !== void 0 ? { exitCode: failed.exitCode } : {},
|
|
60640
|
+
...failed.failureKind !== void 0 ? { failureKind: failed.failureKind } : summaryFailureKind !== void 0 ? { failureKind: summaryFailureKind } : {},
|
|
60641
|
+
...outputTail ? { outputTail } : {}
|
|
60642
|
+
};
|
|
60643
|
+
}
|
|
60509
60644
|
function slimRefineEventResult(result) {
|
|
60510
60645
|
const slim = {};
|
|
60511
60646
|
for (const key2 of [
|
|
@@ -60518,7 +60653,12 @@ function slimRefineEventResult(result) {
|
|
|
60518
60653
|
"into",
|
|
60519
60654
|
"terminalKind",
|
|
60520
60655
|
"nextStep",
|
|
60521
|
-
"finalBranchConvergenceState"
|
|
60656
|
+
"finalBranchConvergenceState",
|
|
60657
|
+
// QW4: merge conflict paths; QW5: cleanup branch-ref / residue warnings.
|
|
60658
|
+
"conflictPaths",
|
|
60659
|
+
"branchRefWarning",
|
|
60660
|
+
"residueWarning",
|
|
60661
|
+
"branchRefDeleted"
|
|
60522
60662
|
]) {
|
|
60523
60663
|
if (result[key2] !== void 0) slim[key2] = result[key2];
|
|
60524
60664
|
}
|
|
@@ -60527,12 +60667,15 @@ function slimRefineEventResult(result) {
|
|
|
60527
60667
|
}
|
|
60528
60668
|
if (result.validationSummary && typeof result.validationSummary === "object") {
|
|
60529
60669
|
const vs = result.validationSummary;
|
|
60670
|
+
const diagnostics = vs.status === "failed" ? extractValidationFailureDiagnostics(vs) : void 0;
|
|
60530
60671
|
slim.validationSummary = {
|
|
60531
60672
|
status: vs.status,
|
|
60532
60673
|
failureCode: vs.failureCode,
|
|
60674
|
+
failureKind: vs.failureKind,
|
|
60533
60675
|
configSource: vs.configSource,
|
|
60534
60676
|
configSourceType: vs.configSourceType,
|
|
60535
|
-
commandsRunCount: Array.isArray(vs.commandsRun) ? vs.commandsRun.length : void 0
|
|
60677
|
+
commandsRunCount: Array.isArray(vs.commandsRun) ? vs.commandsRun.length : void 0,
|
|
60678
|
+
...diagnostics ? { failure: diagnostics } : {}
|
|
60536
60679
|
};
|
|
60537
60680
|
}
|
|
60538
60681
|
if (result.patchEquivalence && typeof result.patchEquivalence === "object") {
|
|
@@ -60673,6 +60816,8 @@ async function executeMeshRefineNodeSynchronously(self, meshId, nodeId, args) {
|
|
|
60673
60816
|
const resolved = await refineResolveRefsStage(self, meshId, nodeId, args, refineStages);
|
|
60674
60817
|
if (resolved.kind === "terminal") return resolved.result;
|
|
60675
60818
|
const ctx = resolved.ctx;
|
|
60819
|
+
const syncBase = await refineSyncBaseStage(self, ctx);
|
|
60820
|
+
if (syncBase.kind === "terminal") return syncBase.result;
|
|
60676
60821
|
const validation = await refineValidationStage(self, ctx);
|
|
60677
60822
|
if (validation.kind === "terminal") return validation.result;
|
|
60678
60823
|
const patchEquivalence = await refinePatchEquivalenceStage(self, ctx);
|
|
@@ -60761,6 +60906,152 @@ async function refineResolveRefsStage(self, meshId, nodeId, args, refineStages)
|
|
|
60761
60906
|
}
|
|
60762
60907
|
};
|
|
60763
60908
|
}
|
|
60909
|
+
async function computeBranchBaseDivergence(execFileAsync4, cwd, baseHead, branchHead) {
|
|
60910
|
+
let mergeBase;
|
|
60911
|
+
try {
|
|
60912
|
+
const { stdout } = await execFileAsync4("git", ["merge-base", baseHead, branchHead], { cwd, encoding: "utf8" });
|
|
60913
|
+
mergeBase = stdout.trim() || void 0;
|
|
60914
|
+
} catch {
|
|
60915
|
+
}
|
|
60916
|
+
let ahead = 0;
|
|
60917
|
+
let behind = 0;
|
|
60918
|
+
try {
|
|
60919
|
+
const { stdout } = await execFileAsync4("git", ["rev-list", "--left-right", "--count", `${baseHead}...${branchHead}`], { cwd, encoding: "utf8" });
|
|
60920
|
+
const [left, right] = stdout.trim().split(/\s+/).map((n) => Number.parseInt(n, 10));
|
|
60921
|
+
behind = Number.isFinite(left) ? left : 0;
|
|
60922
|
+
ahead = Number.isFinite(right) ? right : 0;
|
|
60923
|
+
} catch {
|
|
60924
|
+
}
|
|
60925
|
+
return {
|
|
60926
|
+
mergeBase,
|
|
60927
|
+
ahead,
|
|
60928
|
+
behind,
|
|
60929
|
+
diverged: ahead > 0 && behind > 0,
|
|
60930
|
+
// Strictly behind = base is a descendant of branch (branch is an ancestor of base):
|
|
60931
|
+
// behind>0 with ahead===0.
|
|
60932
|
+
isStrictlyBehind: behind > 0 && ahead === 0
|
|
60933
|
+
};
|
|
60934
|
+
}
|
|
60935
|
+
async function refineSyncBaseStage(self, ctx) {
|
|
60936
|
+
const { repoRoot, baseHead, node, branch, baseBranch, refineStages, execFileAsync: execFileAsync4 } = ctx;
|
|
60937
|
+
let branchHead = ctx.branchHead;
|
|
60938
|
+
const syncStarted = Date.now();
|
|
60939
|
+
const divergence = await computeBranchBaseDivergence(execFileAsync4, node.workspace, baseHead, branchHead);
|
|
60940
|
+
if (divergence.behind === 0) {
|
|
60941
|
+
recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
|
|
60942
|
+
ahead: divergence.ahead,
|
|
60943
|
+
behind: divergence.behind,
|
|
60944
|
+
rebased: false,
|
|
60945
|
+
reason: "branch_up_to_date_with_base"
|
|
60946
|
+
});
|
|
60947
|
+
return { kind: "continue", ctx };
|
|
60948
|
+
}
|
|
60949
|
+
try {
|
|
60950
|
+
const preRebasePe = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
60951
|
+
const alreadyMerged = !preRebasePe.actualPatchId && !!preRebasePe.expectedPatchId;
|
|
60952
|
+
const submoduleConflict = preRebasePe.actionableHint?.kind === "submodule_conflict";
|
|
60953
|
+
if (alreadyMerged || submoduleConflict) {
|
|
60954
|
+
recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
|
|
60955
|
+
ahead: divergence.ahead,
|
|
60956
|
+
behind: divergence.behind,
|
|
60957
|
+
rebased: false,
|
|
60958
|
+
reason: alreadyMerged ? "already_merged_via_other_path_skip_rebase" : "submodule_conflict_defer_to_patch_equivalence"
|
|
60959
|
+
});
|
|
60960
|
+
return { kind: "continue", ctx };
|
|
60961
|
+
}
|
|
60962
|
+
} catch {
|
|
60963
|
+
}
|
|
60964
|
+
const rebaseStarted = Date.now();
|
|
60965
|
+
try {
|
|
60966
|
+
(0, import_node_child_process7.execFileSync)("git", ["rebase", baseHead], { cwd: node.workspace, stdio: ["ignore", "pipe", "pipe"] });
|
|
60967
|
+
} catch (rebaseErr) {
|
|
60968
|
+
try {
|
|
60969
|
+
(0, import_node_child_process7.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
|
|
60970
|
+
} catch {
|
|
60971
|
+
}
|
|
60972
|
+
let submoduleHintPatchEquivalence;
|
|
60973
|
+
try {
|
|
60974
|
+
submoduleHintPatchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, ctx.branchHead);
|
|
60975
|
+
} catch {
|
|
60976
|
+
}
|
|
60977
|
+
const submoduleConflict = submoduleHintPatchEquivalence?.actionableHint?.kind === "submodule_conflict";
|
|
60978
|
+
recordMeshRefineStage(refineStages, "sync_base", "failed", syncStarted, {
|
|
60979
|
+
ahead: divergence.ahead,
|
|
60980
|
+
behind: divergence.behind,
|
|
60981
|
+
diverged: divergence.diverged,
|
|
60982
|
+
error: rebaseErr?.message || String(rebaseErr),
|
|
60983
|
+
...submoduleConflict ? { submoduleConflict: true } : {}
|
|
60984
|
+
});
|
|
60985
|
+
if (submoduleConflict && submoduleHintPatchEquivalence) {
|
|
60986
|
+
recordMeshRefineStage(refineStages, "patch_equivalence", "failed", rebaseStarted, {
|
|
60987
|
+
equivalent: submoduleHintPatchEquivalence.equivalent,
|
|
60988
|
+
expectedPatchId: submoduleHintPatchEquivalence.expectedPatchId,
|
|
60989
|
+
actualPatchId: submoduleHintPatchEquivalence.actualPatchId,
|
|
60990
|
+
error: submoduleHintPatchEquivalence.error,
|
|
60991
|
+
actionableHint: submoduleHintPatchEquivalence.actionableHint
|
|
60992
|
+
});
|
|
60993
|
+
return { kind: "terminal", result: {
|
|
60994
|
+
success: false,
|
|
60995
|
+
code: "patch_equivalence_failed",
|
|
60996
|
+
convergenceStatus: "blocked_review",
|
|
60997
|
+
error: "Refinery patch-equivalence preflight failed (submodule gitlink conflict); merge/refine was not attempted.",
|
|
60998
|
+
branch,
|
|
60999
|
+
into: baseBranch,
|
|
61000
|
+
patchEquivalence: submoduleHintPatchEquivalence,
|
|
61001
|
+
refineStages,
|
|
61002
|
+
finalBranchConvergenceState: {
|
|
61003
|
+
branch,
|
|
61004
|
+
baseBranch,
|
|
61005
|
+
merged: false,
|
|
61006
|
+
removed: false,
|
|
61007
|
+
patchEquivalence: "failed",
|
|
61008
|
+
status: "blocked_review"
|
|
61009
|
+
}
|
|
61010
|
+
} };
|
|
61011
|
+
}
|
|
61012
|
+
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", "failed", rebaseStarted, {
|
|
61013
|
+
error: rebaseErr?.message || String(rebaseErr)
|
|
61014
|
+
});
|
|
61015
|
+
return { kind: "terminal", result: {
|
|
61016
|
+
success: false,
|
|
61017
|
+
code: "needs_rebase_with_conflicts",
|
|
61018
|
+
convergenceStatus: "blocked_review",
|
|
61019
|
+
error: divergence.diverged ? `Branch has diverged from ${baseBranch} (ahead ${divergence.ahead}, behind ${divergence.behind}) and auto-rebase onto the fetched base hit conflicts; resolve conflicts manually and retry.` : `Branch is behind ${baseBranch} and auto-rebase failed due to conflicts; resolve conflicts manually and retry.`,
|
|
61020
|
+
branch,
|
|
61021
|
+
into: baseBranch,
|
|
61022
|
+
refineStages,
|
|
61023
|
+
finalBranchConvergenceState: {
|
|
61024
|
+
branch,
|
|
61025
|
+
baseBranch,
|
|
61026
|
+
merged: false,
|
|
61027
|
+
removed: false,
|
|
61028
|
+
status: "blocked_review"
|
|
61029
|
+
}
|
|
61030
|
+
} };
|
|
61031
|
+
}
|
|
61032
|
+
const { stdout: rebasedHeadStdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
|
|
61033
|
+
branchHead = rebasedHeadStdout.trim();
|
|
61034
|
+
ctx.branchHead = branchHead;
|
|
61035
|
+
let changeImpact = ctx.changeImpact;
|
|
61036
|
+
try {
|
|
61037
|
+
changeImpact = await classifyChangedPackages(node.workspace, baseHead, branchHead);
|
|
61038
|
+
ctx.changeImpact = changeImpact;
|
|
61039
|
+
} catch {
|
|
61040
|
+
}
|
|
61041
|
+
recordMeshRefineStage(refineStages, "sync_base", "passed", syncStarted, {
|
|
61042
|
+
ahead: divergence.ahead,
|
|
61043
|
+
behind: divergence.behind,
|
|
61044
|
+
diverged: divergence.diverged,
|
|
61045
|
+
rebased: true,
|
|
61046
|
+
rebasedBranchHead: branchHead,
|
|
61047
|
+
...changeImpact ? { changeImpact } : {}
|
|
61048
|
+
});
|
|
61049
|
+
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", "passed", rebaseStarted, {
|
|
61050
|
+
rebasedBranchHead: branchHead,
|
|
61051
|
+
rebasedOnto: baseHead
|
|
61052
|
+
});
|
|
61053
|
+
return { kind: "continue", ctx };
|
|
61054
|
+
}
|
|
60764
61055
|
async function refineValidationStage(self, ctx) {
|
|
60765
61056
|
const { mesh, node, branch, baseBranch, refineStages } = ctx;
|
|
60766
61057
|
const validationStarted = Date.now();
|
|
@@ -60785,7 +61076,7 @@ async function refineValidationStage(self, ctx) {
|
|
|
60785
61076
|
{ validationStatus: validationSummary.status, commandsRun: validationSummary.commandsRun.length }
|
|
60786
61077
|
);
|
|
60787
61078
|
if (validationSummary.status === "failed") {
|
|
60788
|
-
const firstFailedCmd = Array.isArray(validationSummary.commandsRun) ? validationSummary.commandsRun.find((c) => c.
|
|
61079
|
+
const firstFailedCmd = Array.isArray(validationSummary.commandsRun) ? validationSummary.commandsRun.find((c) => c.passed === false) : void 0;
|
|
60789
61080
|
const buildValidationFailedError = () => {
|
|
60790
61081
|
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.";
|
|
60791
61082
|
if (!firstFailedCmd) return base;
|
|
@@ -60841,10 +61132,10 @@ ${tail}` : ""
|
|
|
60841
61132
|
return { kind: "continue", ctx };
|
|
60842
61133
|
}
|
|
60843
61134
|
async function refinePatchEquivalenceStage(self, ctx) {
|
|
60844
|
-
const { meshId, nodeId, args, repoRoot, baseHead,
|
|
60845
|
-
|
|
61135
|
+
const { meshId, nodeId, args, repoRoot, baseHead, branch, baseBranch, validationSummary, refineStages } = ctx;
|
|
61136
|
+
const branchHead = ctx.branchHead;
|
|
60846
61137
|
const patchEquivalenceStarted = Date.now();
|
|
60847
|
-
|
|
61138
|
+
const patchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
60848
61139
|
recordMeshRefineStage(refineStages, "patch_equivalence", patchEquivalence.status, patchEquivalenceStarted, {
|
|
60849
61140
|
equivalent: patchEquivalence.equivalent,
|
|
60850
61141
|
expectedPatchId: patchEquivalence.expectedPatchId,
|
|
@@ -60853,90 +61144,8 @@ async function refinePatchEquivalenceStage(self, ctx) {
|
|
|
60853
61144
|
actionableHint: patchEquivalence.actionableHint
|
|
60854
61145
|
});
|
|
60855
61146
|
if (!patchEquivalence.equivalent) {
|
|
60856
|
-
let didAutoRebase = false;
|
|
60857
|
-
let isBehindBase = false;
|
|
60858
|
-
try {
|
|
60859
|
-
(0, import_node_child_process7.execFileSync)("git", ["merge-base", "--is-ancestor", branchHead, baseHead], {
|
|
60860
|
-
cwd: node.workspace,
|
|
60861
|
-
stdio: "ignore"
|
|
60862
|
-
});
|
|
60863
|
-
isBehindBase = true;
|
|
60864
|
-
} catch {
|
|
60865
|
-
}
|
|
60866
|
-
if (isBehindBase) {
|
|
60867
|
-
const autoRebaseStarted = Date.now();
|
|
60868
|
-
try {
|
|
60869
|
-
(0, import_node_child_process7.execFileSync)("git", ["rebase", baseHead], {
|
|
60870
|
-
cwd: node.workspace,
|
|
60871
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
60872
|
-
});
|
|
60873
|
-
const { stdout: rebasedHeadStdout } = await execFileAsync4("git", ["rev-parse", "HEAD"], { cwd: node.workspace, encoding: "utf8" });
|
|
60874
|
-
branchHead = rebasedHeadStdout.trim();
|
|
60875
|
-
const rebasedPatchEquivalence = await runMeshRefinePatchEquivalenceGate(repoRoot, baseHead, branchHead);
|
|
60876
|
-
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", rebasedPatchEquivalence.status, autoRebaseStarted, {
|
|
60877
|
-
equivalent: rebasedPatchEquivalence.equivalent,
|
|
60878
|
-
expectedPatchId: rebasedPatchEquivalence.expectedPatchId,
|
|
60879
|
-
actualPatchId: rebasedPatchEquivalence.actualPatchId,
|
|
60880
|
-
error: rebasedPatchEquivalence.error,
|
|
60881
|
-
rebasedBranchHead: branchHead
|
|
60882
|
-
});
|
|
60883
|
-
if (rebasedPatchEquivalence.equivalent) {
|
|
60884
|
-
patchEquivalence = rebasedPatchEquivalence;
|
|
60885
|
-
didAutoRebase = true;
|
|
60886
|
-
} else {
|
|
60887
|
-
return { kind: "terminal", result: {
|
|
60888
|
-
success: false,
|
|
60889
|
-
code: "needs_rebase",
|
|
60890
|
-
convergenceStatus: "blocked_review",
|
|
60891
|
-
error: "Branch was rebased onto base but patch equivalence still failed; manual intervention required.",
|
|
60892
|
-
branch,
|
|
60893
|
-
into: baseBranch,
|
|
60894
|
-
validationSummary,
|
|
60895
|
-
patchEquivalence: rebasedPatchEquivalence,
|
|
60896
|
-
refineStages,
|
|
60897
|
-
finalBranchConvergenceState: {
|
|
60898
|
-
branch,
|
|
60899
|
-
baseBranch,
|
|
60900
|
-
merged: false,
|
|
60901
|
-
removed: false,
|
|
60902
|
-
validation: "passed",
|
|
60903
|
-
patchEquivalence: "failed",
|
|
60904
|
-
status: "blocked_review"
|
|
60905
|
-
}
|
|
60906
|
-
} };
|
|
60907
|
-
}
|
|
60908
|
-
} catch (rebaseErr) {
|
|
60909
|
-
try {
|
|
60910
|
-
(0, import_node_child_process7.execFileSync)("git", ["rebase", "--abort"], { cwd: node.workspace, stdio: "ignore" });
|
|
60911
|
-
} catch {
|
|
60912
|
-
}
|
|
60913
|
-
recordMeshRefineStage(refineStages, "patch_equivalence_after_auto_rebase", "failed", autoRebaseStarted, {
|
|
60914
|
-
error: rebaseErr?.message || String(rebaseErr)
|
|
60915
|
-
});
|
|
60916
|
-
return { kind: "terminal", result: {
|
|
60917
|
-
success: false,
|
|
60918
|
-
code: "needs_rebase_with_conflicts",
|
|
60919
|
-
convergenceStatus: "blocked_review",
|
|
60920
|
-
error: "Branch is behind base and auto-rebase failed due to conflicts; resolve conflicts manually and retry.",
|
|
60921
|
-
branch,
|
|
60922
|
-
into: baseBranch,
|
|
60923
|
-
validationSummary,
|
|
60924
|
-
patchEquivalence,
|
|
60925
|
-
refineStages,
|
|
60926
|
-
finalBranchConvergenceState: {
|
|
60927
|
-
branch,
|
|
60928
|
-
baseBranch,
|
|
60929
|
-
merged: false,
|
|
60930
|
-
removed: false,
|
|
60931
|
-
validation: "passed",
|
|
60932
|
-
patchEquivalence: "failed",
|
|
60933
|
-
status: "blocked_review"
|
|
60934
|
-
}
|
|
60935
|
-
} };
|
|
60936
|
-
}
|
|
60937
|
-
}
|
|
60938
61147
|
const alreadyMergedViaOtherPath = !patchEquivalence.actualPatchId && !!patchEquivalence.expectedPatchId;
|
|
60939
|
-
if (!
|
|
61148
|
+
if (!alreadyMergedViaOtherPath) {
|
|
60940
61149
|
return { kind: "terminal", result: {
|
|
60941
61150
|
success: false,
|
|
60942
61151
|
code: "patch_equivalence_failed",
|
|
@@ -60958,7 +61167,7 @@ async function refinePatchEquivalenceStage(self, ctx) {
|
|
|
60958
61167
|
}
|
|
60959
61168
|
} };
|
|
60960
61169
|
}
|
|
60961
|
-
|
|
61170
|
+
{
|
|
60962
61171
|
recordMeshRefineStage(refineStages, "merge", "skipped", Date.now(), {
|
|
60963
61172
|
reason: "already_merged_via_other_path",
|
|
60964
61173
|
note: "actualPatchId is empty; branch content is already present in base via a different commit path"
|
|
@@ -61166,8 +61375,145 @@ ${hintLines.join("\n")}` : "",
|
|
|
61166
61375
|
}
|
|
61167
61376
|
return { kind: "continue", ctx };
|
|
61168
61377
|
}
|
|
61378
|
+
async function requestCoordinatorLocalCatchup(self, params) {
|
|
61379
|
+
const { meshId, ctx, mesh, baseBranch, repoRoot } = params;
|
|
61380
|
+
const coordinatorDaemonId = typeof ctx.args?.coordinatorDaemonId === "string" && ctx.args.coordinatorDaemonId.trim() ? ctx.args.coordinatorDaemonId.trim() : self.deps.statusInstanceId || void 0;
|
|
61381
|
+
if (!coordinatorDaemonId) return void 0;
|
|
61382
|
+
if (!Array.isArray(mesh?.nodes)) return void 0;
|
|
61383
|
+
const coordinatorBaseNode = mesh.nodes.find((n) => !n?.isLocalWorktree && daemonIdListIncludes([coordinatorDaemonId], readStringValue(n?.daemonId)));
|
|
61384
|
+
if (!coordinatorBaseNode) return void 0;
|
|
61385
|
+
const coordinatorWorkspace = readStringValue(coordinatorBaseNode.repoRoot) || readStringValue(coordinatorBaseNode.workspace);
|
|
61386
|
+
if (!coordinatorWorkspace) return void 0;
|
|
61387
|
+
const drainIds = [self.deps.statusInstanceId].filter((v) => typeof v === "string" && v.length > 0);
|
|
61388
|
+
const selfIds = resolveCoordinatorSelfIds(mesh, drainIds);
|
|
61389
|
+
const coordinatorIsSelf = daemonIdListIncludes(selfIds, readStringValue(coordinatorBaseNode.daemonId));
|
|
61390
|
+
if (coordinatorIsSelf) {
|
|
61391
|
+
try {
|
|
61392
|
+
const ff = await fastForwardMeshNode({
|
|
61393
|
+
meshId,
|
|
61394
|
+
nodeId: readStringValue(coordinatorBaseNode.id),
|
|
61395
|
+
workspace: coordinatorWorkspace,
|
|
61396
|
+
branch: baseBranch,
|
|
61397
|
+
mode: "merge",
|
|
61398
|
+
execute: true,
|
|
61399
|
+
trigger: "refine_post_push_catchup",
|
|
61400
|
+
allowAutoPublishSubmoduleMainCommits: mesh?.policy?.allowAutoPublishSubmoduleMainCommits === true
|
|
61401
|
+
});
|
|
61402
|
+
return {
|
|
61403
|
+
mode: "local_fast_forward",
|
|
61404
|
+
coordinatorWorkspace,
|
|
61405
|
+
sameAsRepoRoot: coordinatorWorkspace === repoRoot,
|
|
61406
|
+
code: ff.code,
|
|
61407
|
+
executed: ff.executed,
|
|
61408
|
+
success: ff.success,
|
|
61409
|
+
...ff.blockingReasons?.length ? { blockingReasons: ff.blockingReasons } : {}
|
|
61410
|
+
};
|
|
61411
|
+
} catch (e) {
|
|
61412
|
+
return { mode: "local_fast_forward", coordinatorWorkspace, error: e?.message || String(e) };
|
|
61413
|
+
}
|
|
61414
|
+
}
|
|
61415
|
+
try {
|
|
61416
|
+
queuePendingMeshCoordinatorEvent({
|
|
61417
|
+
event: "coordinator_catchup",
|
|
61418
|
+
meshId,
|
|
61419
|
+
nodeLabel: readStringValue(coordinatorBaseNode.id) || "coordinator-base",
|
|
61420
|
+
nodeId: readStringValue(coordinatorBaseNode.id),
|
|
61421
|
+
workspace: coordinatorWorkspace,
|
|
61422
|
+
metadataEvent: {
|
|
61423
|
+
source: "refine_post_push_coordinator_catchup",
|
|
61424
|
+
operation: "coordinator_catchup",
|
|
61425
|
+
baseBranch,
|
|
61426
|
+
coordinatorDaemonId,
|
|
61427
|
+
reason: "post_push_base_advanced"
|
|
61428
|
+
},
|
|
61429
|
+
queuedAt: Date.now(),
|
|
61430
|
+
targetCoordinatorDaemonId: coordinatorDaemonId
|
|
61431
|
+
});
|
|
61432
|
+
return { mode: "pending_marker_queued", coordinatorDaemonId, coordinatorWorkspace, baseBranch };
|
|
61433
|
+
} catch (e) {
|
|
61434
|
+
return { mode: "pending_marker_queued", coordinatorDaemonId, error: e?.message || String(e) };
|
|
61435
|
+
}
|
|
61436
|
+
}
|
|
61169
61437
|
async function refineMergeAndFinalizeStage(self, ctx) {
|
|
61170
61438
|
const { meshId, nodeId, args, repoRoot, baseHead, node, branch, baseBranch, sourceNode, validationSummary, patchEquivalence, submoduleReachability, mesh, refineStages, execFileAsync: execFileAsync4 } = ctx;
|
|
61439
|
+
const leaseKey = `${repoRoot}::${baseBranch}`;
|
|
61440
|
+
const leaseHolder = buildRefineJobKey(self, meshId, nodeId);
|
|
61441
|
+
if (self.refineBaseLeases.has(leaseKey) && self.refineBaseLeases.get(leaseKey) !== leaseHolder) {
|
|
61442
|
+
recordMeshRefineStage(refineStages, "base_lease", "skipped", Date.now(), {
|
|
61443
|
+
leaseKey,
|
|
61444
|
+
heldBy: self.refineBaseLeases.get(leaseKey),
|
|
61445
|
+
retryable: true
|
|
61446
|
+
});
|
|
61447
|
+
return { kind: "terminal", result: {
|
|
61448
|
+
success: false,
|
|
61449
|
+
code: "base_locked",
|
|
61450
|
+
convergenceStatus: "blocked_review",
|
|
61451
|
+
retryable: true,
|
|
61452
|
+
error: `Another refine holds the base lease for ${baseBranch} in this repo; retry after it completes.`,
|
|
61453
|
+
branch,
|
|
61454
|
+
into: baseBranch,
|
|
61455
|
+
validationSummary,
|
|
61456
|
+
patchEquivalence,
|
|
61457
|
+
submoduleReachability,
|
|
61458
|
+
refineStages,
|
|
61459
|
+
finalBranchConvergenceState: {
|
|
61460
|
+
branch,
|
|
61461
|
+
baseBranch,
|
|
61462
|
+
merged: false,
|
|
61463
|
+
removed: false,
|
|
61464
|
+
status: "blocked_review"
|
|
61465
|
+
}
|
|
61466
|
+
} };
|
|
61467
|
+
}
|
|
61468
|
+
self.refineBaseLeases.set(leaseKey, leaseHolder);
|
|
61469
|
+
try {
|
|
61470
|
+
return await runRefineMergeAndFinalizeLocked(self, ctx);
|
|
61471
|
+
} finally {
|
|
61472
|
+
if (self.refineBaseLeases.get(leaseKey) === leaseHolder) self.refineBaseLeases.delete(leaseKey);
|
|
61473
|
+
}
|
|
61474
|
+
}
|
|
61475
|
+
async function runRefineMergeAndFinalizeLocked(self, ctx) {
|
|
61476
|
+
const { meshId, nodeId, args, repoRoot, baseHead, node, branch, baseBranch, sourceNode, validationSummary, patchEquivalence, submoduleReachability, mesh, refineStages, execFileAsync: execFileAsync4 } = ctx;
|
|
61477
|
+
const casStarted = Date.now();
|
|
61478
|
+
let baseMoved = false;
|
|
61479
|
+
let liveBaseHead;
|
|
61480
|
+
try {
|
|
61481
|
+
await execFileAsync4("git", ["fetch", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
61482
|
+
const { stdout } = await execFileAsync4("git", ["rev-parse", `origin/${baseBranch}`], { cwd: repoRoot, encoding: "utf8" });
|
|
61483
|
+
liveBaseHead = stdout.trim();
|
|
61484
|
+
baseMoved = !!liveBaseHead && liveBaseHead !== baseHead;
|
|
61485
|
+
} catch {
|
|
61486
|
+
}
|
|
61487
|
+
if (baseMoved) {
|
|
61488
|
+
recordMeshRefineStage(refineStages, "base_cas", "failed", casStarted, {
|
|
61489
|
+
pinnedBaseHead: baseHead,
|
|
61490
|
+
liveBaseHead,
|
|
61491
|
+
retryable: true
|
|
61492
|
+
});
|
|
61493
|
+
return { kind: "terminal", result: {
|
|
61494
|
+
success: false,
|
|
61495
|
+
code: "base_moved",
|
|
61496
|
+
convergenceStatus: "blocked_review",
|
|
61497
|
+
retryable: true,
|
|
61498
|
+
error: `Base ${baseBranch} advanced from ${baseHead.slice(0, 7)} to ${(liveBaseHead || "").slice(0, 7)} after this node was validated; re-run refine to rebase onto and re-validate the new base.`,
|
|
61499
|
+
branch,
|
|
61500
|
+
into: baseBranch,
|
|
61501
|
+
pinnedBaseHead: baseHead,
|
|
61502
|
+
liveBaseHead,
|
|
61503
|
+
validationSummary,
|
|
61504
|
+
patchEquivalence,
|
|
61505
|
+
submoduleReachability,
|
|
61506
|
+
refineStages,
|
|
61507
|
+
finalBranchConvergenceState: {
|
|
61508
|
+
branch,
|
|
61509
|
+
baseBranch,
|
|
61510
|
+
merged: false,
|
|
61511
|
+
removed: false,
|
|
61512
|
+
status: "blocked_review"
|
|
61513
|
+
}
|
|
61514
|
+
} };
|
|
61515
|
+
}
|
|
61516
|
+
recordMeshRefineStage(refineStages, "base_cas", "passed", casStarted, { pinnedBaseHead: baseHead });
|
|
61171
61517
|
let mergeResult;
|
|
61172
61518
|
const mergeStarted = Date.now();
|
|
61173
61519
|
try {
|
|
@@ -61179,16 +61525,33 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61179
61525
|
};
|
|
61180
61526
|
recordMeshRefineStage(refineStages, "merge", "passed", mergeStarted, mergeResult);
|
|
61181
61527
|
} catch (e) {
|
|
61528
|
+
const mergeOutput = `${e?.stdout || ""}
|
|
61529
|
+
${e?.stderr || ""}`;
|
|
61530
|
+
const conflictPaths = [...mergeOutput.matchAll(/Merge conflict in (.+)/g)].map((m) => m[1].trim()).filter(Boolean);
|
|
61531
|
+
try {
|
|
61532
|
+
await execFileAsync4("git", ["merge", "--abort"], { cwd: repoRoot, encoding: "utf8" });
|
|
61533
|
+
} catch {
|
|
61534
|
+
}
|
|
61182
61535
|
recordMeshRefineStage(refineStages, "merge", "failed", mergeStarted, {
|
|
61183
61536
|
error: e?.message || String(e),
|
|
61184
61537
|
stdout: truncateValidationOutput(e?.stdout),
|
|
61185
|
-
stderr: truncateValidationOutput(e?.stderr)
|
|
61538
|
+
stderr: truncateValidationOutput(e?.stderr),
|
|
61539
|
+
...conflictPaths.length ? { conflictPaths } : {}
|
|
61186
61540
|
});
|
|
61187
61541
|
return { kind: "terminal", result: {
|
|
61188
61542
|
success: false,
|
|
61189
|
-
|
|
61543
|
+
code: "merge_failed",
|
|
61544
|
+
convergenceStatus: "not_mergeable",
|
|
61545
|
+
error: conflictPaths.length ? `Merge failed \u2014 conflicts in ${conflictPaths.length} path(s): ${conflictPaths.join(", ")}. The branch cannot fast-forward-merge onto ${baseBranch}; resolve conflicts (rebase the branch onto the fetched base) and retry.` : `Merge failed (conflicts?): ${e?.message || String(e)}`,
|
|
61546
|
+
branch,
|
|
61547
|
+
into: baseBranch,
|
|
61548
|
+
...conflictPaths.length ? { conflictPaths } : {},
|
|
61190
61549
|
validationSummary,
|
|
61191
61550
|
patchEquivalence,
|
|
61551
|
+
mergeResult: {
|
|
61552
|
+
stdout: truncateValidationOutput(e?.stdout),
|
|
61553
|
+
stderr: truncateValidationOutput(e?.stderr)
|
|
61554
|
+
},
|
|
61192
61555
|
refineStages,
|
|
61193
61556
|
finalBranchConvergenceState: {
|
|
61194
61557
|
branch,
|
|
@@ -61244,6 +61607,93 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61244
61607
|
}
|
|
61245
61608
|
} };
|
|
61246
61609
|
}
|
|
61610
|
+
const requireApprovalForPush = mesh?.policy?.requireApprovalForPush ?? DEFAULT_MESH_POLICY.requireApprovalForPush;
|
|
61611
|
+
let pushResult;
|
|
61612
|
+
if (!requireApprovalForPush) {
|
|
61613
|
+
const pushStarted = Date.now();
|
|
61614
|
+
try {
|
|
61615
|
+
await execFileAsync4("git", ["push", "origin", baseBranch], { cwd: repoRoot, encoding: "utf8" });
|
|
61616
|
+
pushResult = { pushed: true, remote: "origin", branch: baseBranch, durationMs: Date.now() - pushStarted };
|
|
61617
|
+
recordMeshRefineStage(refineStages, "push", "passed", pushStarted, pushResult);
|
|
61618
|
+
} catch (e) {
|
|
61619
|
+
pushResult = {
|
|
61620
|
+
pushed: false,
|
|
61621
|
+
remote: "origin",
|
|
61622
|
+
branch: baseBranch,
|
|
61623
|
+
error: e?.message || String(e),
|
|
61624
|
+
stderr: e?.stderr,
|
|
61625
|
+
durationMs: Date.now() - pushStarted
|
|
61626
|
+
};
|
|
61627
|
+
recordMeshRefineStage(refineStages, "push", "failed", pushStarted, pushResult);
|
|
61628
|
+
return { kind: "terminal", result: {
|
|
61629
|
+
success: false,
|
|
61630
|
+
code: "push_failed",
|
|
61631
|
+
convergenceStatus: "blocked_review",
|
|
61632
|
+
retryable: true,
|
|
61633
|
+
merged: true,
|
|
61634
|
+
mergedLocal: true,
|
|
61635
|
+
pushed: false,
|
|
61636
|
+
error: `Refinery merged '${branch}' into local ${baseBranch} but the push to origin failed; the worktree and branch ref were preserved (NOT cleaned up) so the push can be retried. Run: git -C ${repoRoot} push origin ${baseBranch}`,
|
|
61637
|
+
branch,
|
|
61638
|
+
into: baseBranch,
|
|
61639
|
+
pushResult,
|
|
61640
|
+
pushCommand: `git push origin ${baseBranch}`,
|
|
61641
|
+
validationSummary,
|
|
61642
|
+
patchEquivalence,
|
|
61643
|
+
submoduleReachability,
|
|
61644
|
+
submoduleAlignment,
|
|
61645
|
+
mergeResult,
|
|
61646
|
+
refineStages,
|
|
61647
|
+
finalBranchConvergenceState: {
|
|
61648
|
+
branch: baseBranch,
|
|
61649
|
+
mergedBranch: branch,
|
|
61650
|
+
baseBranch,
|
|
61651
|
+
merged: true,
|
|
61652
|
+
pushed: false,
|
|
61653
|
+
removed: false,
|
|
61654
|
+
validation: "passed",
|
|
61655
|
+
patchEquivalence: "passed",
|
|
61656
|
+
submoduleAlignment: submoduleAlignment.status,
|
|
61657
|
+
status: "merged_push_failed",
|
|
61658
|
+
nextStep: `Retry the push (git -C ${repoRoot} push origin ${baseBranch}); then the worktree can be cleaned up.`
|
|
61659
|
+
}
|
|
61660
|
+
} };
|
|
61661
|
+
}
|
|
61662
|
+
} else {
|
|
61663
|
+
recordMeshRefineStage(refineStages, "push", "skipped", Date.now(), {
|
|
61664
|
+
reason: "require_approval_for_push"
|
|
61665
|
+
});
|
|
61666
|
+
return { kind: "terminal", result: {
|
|
61667
|
+
success: true,
|
|
61668
|
+
merged: true,
|
|
61669
|
+
mergedLocal: true,
|
|
61670
|
+
pushed: false,
|
|
61671
|
+
branch,
|
|
61672
|
+
into: baseBranch,
|
|
61673
|
+
validationSummary,
|
|
61674
|
+
patchEquivalence,
|
|
61675
|
+
submoduleReachability,
|
|
61676
|
+
submoduleAlignment,
|
|
61677
|
+
mergeResult,
|
|
61678
|
+
refineStages,
|
|
61679
|
+
pushReady: true,
|
|
61680
|
+
pushCommand: `git push origin ${baseBranch}`,
|
|
61681
|
+
pushNote: "requireApprovalForPush is enabled \u2014 the merge landed on the local base but was NOT pushed and the worktree was NOT cleaned up. Run the push (or approve it), then re-run refine/cleanup to remove the worktree.",
|
|
61682
|
+
finalBranchConvergenceState: {
|
|
61683
|
+
branch: baseBranch,
|
|
61684
|
+
mergedBranch: branch,
|
|
61685
|
+
baseBranch,
|
|
61686
|
+
merged: true,
|
|
61687
|
+
pushed: false,
|
|
61688
|
+
removed: false,
|
|
61689
|
+
validation: "passed",
|
|
61690
|
+
patchEquivalence: "passed",
|
|
61691
|
+
submoduleAlignment: submoduleAlignment.status,
|
|
61692
|
+
status: "merged_local_pending_push",
|
|
61693
|
+
nextStep: `Approve and run the push (git -C ${repoRoot} push origin ${baseBranch}); the worktree is retained until then.`
|
|
61694
|
+
}
|
|
61695
|
+
} };
|
|
61696
|
+
}
|
|
61247
61697
|
const cleanupStarted = Date.now();
|
|
61248
61698
|
const refineSessionCleanupMode = self.normalizeMeshSessionCleanupMode(
|
|
61249
61699
|
mesh?.policy?.sessionCleanupOnNodeRemove
|
|
@@ -61271,10 +61721,10 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61271
61721
|
sessionCleanupMode: refineSessionCleanupMode,
|
|
61272
61722
|
...refineSessionIds && refineSessionIds.length > 0 ? { sessionIds: refineSessionIds } : {},
|
|
61273
61723
|
inlineMesh: args?.inlineMesh,
|
|
61274
|
-
// REFINE-CLEANUP: refine reaches cleanup only AFTER a verified merge
|
|
61275
|
-
//
|
|
61276
|
-
// (e.g. a bootstrap lockfile rewrite) — never unmerged work.
|
|
61277
|
-
// sets requireClean=false so a plain-dirty worktree no longer aborts
|
|
61724
|
+
// REFINE-CLEANUP: refine reaches cleanup only AFTER a verified merge AND a
|
|
61725
|
+
// successful push (DS1), so any residual worktree dirtiness here is
|
|
61726
|
+
// incidental (e.g. a bootstrap lockfile rewrite) — never unmerged work.
|
|
61727
|
+
// `force` sets requireClean=false so a plain-dirty worktree no longer aborts
|
|
61278
61728
|
// removal with merged_cleanup_failed. Branch-ref deletion still keys off
|
|
61279
61729
|
// mergeConvergence (NOT the force flag), so no merged work can be lost.
|
|
61280
61730
|
force: true
|
|
@@ -61291,7 +61741,7 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61291
61741
|
appendLedgerEntry2(meshId, {
|
|
61292
61742
|
kind: "node_removed",
|
|
61293
61743
|
nodeId,
|
|
61294
|
-
payload: { refined: true, mergedBranch: branch, into: baseBranch, validationSummary, patchEquivalence, submoduleReachability, submoduleAlignment }
|
|
61744
|
+
payload: { refined: true, mergedBranch: branch, into: baseBranch, pushed: true, validationSummary, patchEquivalence, submoduleReachability, submoduleAlignment }
|
|
61295
61745
|
});
|
|
61296
61746
|
recordMeshRefineStage(refineStages, "ledger", "passed", ledgerStarted);
|
|
61297
61747
|
} catch (e) {
|
|
@@ -61303,21 +61753,24 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61303
61753
|
mergedBranch: branch,
|
|
61304
61754
|
baseBranch,
|
|
61305
61755
|
merged: true,
|
|
61756
|
+
pushed: true,
|
|
61306
61757
|
removed: removeResult?.success !== false,
|
|
61307
61758
|
validation: "passed",
|
|
61308
61759
|
patchEquivalence: "passed",
|
|
61309
61760
|
submoduleAlignment: submoduleAlignment.status,
|
|
61310
|
-
status: removeResult?.success === false ? "merged_cleanup_failed" : "
|
|
61761
|
+
status: removeResult?.success === false ? "merged_cleanup_failed" : "merged_pushed"
|
|
61311
61762
|
};
|
|
61312
61763
|
if (removeResult?.success === false) {
|
|
61313
61764
|
return { kind: "terminal", result: {
|
|
61314
61765
|
success: false,
|
|
61315
61766
|
code: "cleanup_failed",
|
|
61316
|
-
error: "Refinery merge completed but worktree cleanup failed; manual cleanup/retry is required.",
|
|
61767
|
+
error: "Refinery merge + push completed but worktree cleanup failed; the change is on origin \u2014 manual worktree cleanup/retry is required.",
|
|
61317
61768
|
merged: true,
|
|
61769
|
+
pushed: true,
|
|
61318
61770
|
branch,
|
|
61319
61771
|
into: baseBranch,
|
|
61320
61772
|
removeResult,
|
|
61773
|
+
pushResult,
|
|
61321
61774
|
validationSummary,
|
|
61322
61775
|
patchEquivalence,
|
|
61323
61776
|
submoduleReachability,
|
|
@@ -61328,33 +61781,35 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61328
61781
|
finalBranchConvergenceState
|
|
61329
61782
|
} };
|
|
61330
61783
|
}
|
|
61331
|
-
|
|
61332
|
-
|
|
61333
|
-
|
|
61334
|
-
|
|
61335
|
-
|
|
61336
|
-
|
|
61337
|
-
|
|
61338
|
-
|
|
61339
|
-
|
|
61340
|
-
|
|
61341
|
-
|
|
61342
|
-
pushed: false,
|
|
61343
|
-
remote: "origin",
|
|
61344
|
-
branch: baseBranch,
|
|
61345
|
-
error: e?.message || String(e),
|
|
61346
|
-
stderr: e?.stderr,
|
|
61347
|
-
durationMs: Date.now() - pushStarted
|
|
61348
|
-
};
|
|
61349
|
-
recordMeshRefineStage(refineStages, "push", "failed", pushStarted, pushResult);
|
|
61784
|
+
let coordinatorCatchup;
|
|
61785
|
+
try {
|
|
61786
|
+
coordinatorCatchup = await requestCoordinatorLocalCatchup(self, {
|
|
61787
|
+
meshId,
|
|
61788
|
+
ctx,
|
|
61789
|
+
mesh,
|
|
61790
|
+
baseBranch,
|
|
61791
|
+
repoRoot
|
|
61792
|
+
});
|
|
61793
|
+
if (coordinatorCatchup) {
|
|
61794
|
+
recordMeshRefineStage(refineStages, "coordinator_catchup", "passed", Date.now(), coordinatorCatchup);
|
|
61350
61795
|
}
|
|
61796
|
+
} catch {
|
|
61351
61797
|
}
|
|
61798
|
+
const cleanupBranchRefWarning = typeof removeResult?.branchRefWarning === "string" ? removeResult.branchRefWarning : void 0;
|
|
61799
|
+
const cleanupResidueWarning = typeof removeResult?.residueWarning === "string" ? removeResult.residueWarning : void 0;
|
|
61800
|
+
const cleanupBranchRefDeleted = typeof removeResult?.worktreeCleanup?.branchRefDeleted === "boolean" ? removeResult.worktreeCleanup.branchRefDeleted : void 0;
|
|
61352
61801
|
return { kind: "terminal", result: {
|
|
61353
61802
|
success: true,
|
|
61354
61803
|
merged: true,
|
|
61804
|
+
pushed: true,
|
|
61355
61805
|
branch,
|
|
61356
61806
|
into: baseBranch,
|
|
61357
61807
|
removeResult,
|
|
61808
|
+
pushResult,
|
|
61809
|
+
...coordinatorCatchup ? { coordinatorCatchup } : {},
|
|
61810
|
+
...cleanupBranchRefWarning ? { branchRefWarning: cleanupBranchRefWarning } : {},
|
|
61811
|
+
...cleanupResidueWarning ? { residueWarning: cleanupResidueWarning } : {},
|
|
61812
|
+
...cleanupBranchRefDeleted !== void 0 ? { branchRefDeleted: cleanupBranchRefDeleted } : {},
|
|
61358
61813
|
validationSummary,
|
|
61359
61814
|
patchEquivalence,
|
|
61360
61815
|
submoduleReachability,
|
|
@@ -61362,13 +61817,7 @@ async function refineMergeAndFinalizeStage(self, ctx) {
|
|
|
61362
61817
|
mergeResult,
|
|
61363
61818
|
refineStages,
|
|
61364
61819
|
...ledgerError ? { ledgerError } : {},
|
|
61365
|
-
finalBranchConvergenceState
|
|
61366
|
-
// Push outcome or readiness info for coordinator.
|
|
61367
|
-
...pushResult ? { pushResult } : {
|
|
61368
|
-
pushReady: true,
|
|
61369
|
-
pushCommand: `git push origin ${baseBranch}`,
|
|
61370
|
-
pushNote: "requireApprovalForPush is enabled \u2014 run the push command or obtain user approval before pushing."
|
|
61371
|
-
}
|
|
61820
|
+
finalBranchConvergenceState
|
|
61372
61821
|
} };
|
|
61373
61822
|
}
|
|
61374
61823
|
async function batchRefineMeshNodes(self, meshId, requestedNodeIds, args) {
|
|
@@ -61523,29 +61972,34 @@ async function batchRefineMeshNodes(self, meshId, requestedNodeIds, args) {
|
|
|
61523
61972
|
}
|
|
61524
61973
|
return runMeshRefineBatchConvergence(self, meshId, orderedNodes, ordering, args);
|
|
61525
61974
|
}
|
|
61975
|
+
var RETRYABLE_BASE_MOVEMENT_CODES = /* @__PURE__ */ new Set(["base_moved", "base_locked"]);
|
|
61976
|
+
function classifyBatchNodeConvergence(result) {
|
|
61977
|
+
const code = typeof result.code === "string" ? result.code : "";
|
|
61978
|
+
const stage = Array.isArray(result.refineStages) ? result.refineStages.filter((s2) => s2.status === "failed").map((s2) => s2.stage).filter(Boolean).pop() : void 0;
|
|
61979
|
+
let convergence;
|
|
61980
|
+
if (code === "already_merged" && result.alreadyMergedViaOtherPath) {
|
|
61981
|
+
convergence = "skipped_patch_equivalent";
|
|
61982
|
+
} else if (result.success === true) {
|
|
61983
|
+
convergence = "merged_to_main";
|
|
61984
|
+
} else if (code === "merge_failed" || stage === "merge") {
|
|
61985
|
+
convergence = "not_mergeable";
|
|
61986
|
+
} else {
|
|
61987
|
+
convergence = "blocked_review";
|
|
61988
|
+
}
|
|
61989
|
+
const retryable = convergence === "blocked_review" && (result.retryable === true || RETRYABLE_BASE_MOVEMENT_CODES.has(code));
|
|
61990
|
+
return { convergence, code, retryable, ...stage ? { stage } : {} };
|
|
61991
|
+
}
|
|
61526
61992
|
async function runMeshRefineBatchConvergence(self, meshId, orderedNodes, ordering, args) {
|
|
61527
|
-
const
|
|
61528
|
-
for (const node of orderedNodes) {
|
|
61993
|
+
const refineOne = async (node) => {
|
|
61529
61994
|
let result;
|
|
61530
61995
|
try {
|
|
61531
61996
|
result = await executeMeshRefineNodeSynchronously(self, meshId, node.id, args);
|
|
61532
61997
|
} catch (e) {
|
|
61533
61998
|
result = { success: false, error: e?.message || String(e) };
|
|
61534
61999
|
}
|
|
61535
|
-
const
|
|
61536
|
-
let convergence;
|
|
61537
|
-
if (code === "already_merged" && result.alreadyMergedViaOtherPath) {
|
|
61538
|
-
convergence = "skipped_patch_equivalent";
|
|
61539
|
-
} else if (result.success === true) {
|
|
61540
|
-
convergence = "merged_to_main";
|
|
61541
|
-
} else if (code === "merge_failed") {
|
|
61542
|
-
convergence = "not_mergeable";
|
|
61543
|
-
} else {
|
|
61544
|
-
convergence = "blocked_review";
|
|
61545
|
-
}
|
|
62000
|
+
const { convergence, code, stage, retryable } = classifyBatchNodeConvergence(result);
|
|
61546
62001
|
const fbcs = result.finalBranchConvergenceState && typeof result.finalBranchConvergenceState === "object" ? result.finalBranchConvergenceState : void 0;
|
|
61547
|
-
|
|
61548
|
-
results.push({
|
|
62002
|
+
return {
|
|
61549
62003
|
nodeId: node.id,
|
|
61550
62004
|
workspace: node.workspace,
|
|
61551
62005
|
convergence,
|
|
@@ -61553,14 +62007,30 @@ async function runMeshRefineBatchConvergence(self, meshId, orderedNodes, orderin
|
|
|
61553
62007
|
...typeof result.blockedReason === "string" ? { reason: result.blockedReason } : {},
|
|
61554
62008
|
...stage ? { stage } : {},
|
|
61555
62009
|
...typeof result.error === "string" ? { error: result.error } : {},
|
|
62010
|
+
...retryable ? { retryable: true } : {},
|
|
61556
62011
|
...fbcs ? { finalBranchConvergenceState: fbcs } : {}
|
|
61557
|
-
}
|
|
62012
|
+
};
|
|
62013
|
+
};
|
|
62014
|
+
const results = [];
|
|
62015
|
+
const retryQueue = [];
|
|
62016
|
+
for (const node of orderedNodes) {
|
|
62017
|
+
const outcome = await refineOne(node);
|
|
62018
|
+
results.push(outcome);
|
|
62019
|
+
if (outcome.retryable) retryQueue.push(node);
|
|
62020
|
+
}
|
|
62021
|
+
for (const node of retryQueue) {
|
|
62022
|
+
const idx = results.findIndex((r) => r.nodeId === node.id);
|
|
62023
|
+
const retried = await refineOne(node);
|
|
62024
|
+
retried.retried = true;
|
|
62025
|
+
if (idx >= 0) results[idx] = retried;
|
|
62026
|
+
else results.push(retried);
|
|
61558
62027
|
}
|
|
61559
62028
|
const summary = {
|
|
61560
62029
|
merged: results.filter((r) => r.convergence === "merged_to_main").length,
|
|
61561
62030
|
skipped: results.filter((r) => r.convergence === "skipped_patch_equivalent").length,
|
|
61562
62031
|
blocked: results.filter((r) => r.convergence === "blocked_review").length,
|
|
61563
|
-
notMergeable: results.filter((r) => r.convergence === "not_mergeable").length
|
|
62032
|
+
notMergeable: results.filter((r) => r.convergence === "not_mergeable").length,
|
|
62033
|
+
...retryQueue.length ? { retried: retryQueue.length } : {}
|
|
61564
62034
|
};
|
|
61565
62035
|
const allConverged = summary.blocked === 0 && summary.notMergeable === 0;
|
|
61566
62036
|
return {
|
|
@@ -61773,7 +62243,7 @@ async function finishMeshRefineJob(self, handle, args) {
|
|
|
61773
62243
|
}
|
|
61774
62244
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
61775
62245
|
const refineCode = typeof result.code === "string" ? result.code : "";
|
|
61776
|
-
const refineTerminalKind = result.success === true ? "completed" : refineCode === "blocked_review" ? "blocked_review" : refineCode === "validation_failed" || refineCode === "validation_dependencies_missing" ? "validation_failed" : refineCode === "submodule_reachability_failed" ? "submodule_reachability_failed" : refineCode === "merge_failed" || refineCode === "patch_equivalence_failed" || refineCode === "needs_rebase" || refineCode === "needs_rebase_with_conflicts" ? "merge_failed" : refineCode === "cleanup_failed" ? "cleanup_failed" : "merge_failed";
|
|
62246
|
+
const refineTerminalKind = result.success === true ? "completed" : refineCode === "blocked_review" ? "blocked_review" : refineCode === "validation_failed" || refineCode === "validation_dependencies_missing" || refineCode === "missing_dependencies" || refineCode === "dependency_bootstrap_failed" || refineCode === "spawn_resolution_failed" || refineCode === "validation_unavailable" ? "validation_failed" : refineCode === "submodule_reachability_failed" ? "submodule_reachability_failed" : refineCode === "merge_failed" || refineCode === "patch_equivalence_failed" || refineCode === "needs_rebase" || refineCode === "needs_rebase_with_conflicts" ? "merge_failed" : refineCode === "cleanup_failed" ? "cleanup_failed" : "merge_failed";
|
|
61777
62247
|
const isTerminalSuccess = refineTerminalKind === "completed";
|
|
61778
62248
|
const blockerContext = isTerminalSuccess ? void 0 : (() => {
|
|
61779
62249
|
const code = typeof result.code === "string" ? result.code : refineTerminalKind;
|
|
@@ -61804,9 +62274,12 @@ async function finishMeshRefineJob(self, handle, args) {
|
|
|
61804
62274
|
}
|
|
61805
62275
|
if (stage === "validation" && result.validationSummary) {
|
|
61806
62276
|
const vs = result.validationSummary;
|
|
62277
|
+
const diagnostics = extractValidationFailureDiagnostics(vs);
|
|
61807
62278
|
ctx.details = {
|
|
61808
62279
|
failureCode: vs.failureCode,
|
|
61809
|
-
|
|
62280
|
+
failureKind: vs.failureKind,
|
|
62281
|
+
commandsRun: Array.isArray(vs.commandsRun) ? vs.commandsRun.length : void 0,
|
|
62282
|
+
...diagnostics ? { failure: diagnostics } : {}
|
|
61810
62283
|
};
|
|
61811
62284
|
}
|
|
61812
62285
|
return ctx;
|
|
@@ -62841,6 +63314,15 @@ var DaemonCommandRouter = class {
|
|
|
62841
63314
|
runningRefineBatchJobs = /* @__PURE__ */ new Map();
|
|
62842
63315
|
/** Terminal async batch Refinery jobs preserve the last batch outcome for late readers. */
|
|
62843
63316
|
terminalRefineBatchJobs = /* @__PURE__ */ new Map();
|
|
63317
|
+
/**
|
|
63318
|
+
* DS2: in-process refinement leases keyed by `${repoRoot}::${baseBranch}`. Serialize
|
|
63319
|
+
* the base-mutating window (candidate-SHA pin → merge → push) of concurrent single-node
|
|
63320
|
+
* refines that target the SAME base branch in the same repo, so two refines cannot both
|
|
63321
|
+
* validate against one baseHead and then race their merges (the base-movement race). The
|
|
63322
|
+
* batch path is already sequential, so this only matters for overlapping single-node
|
|
63323
|
+
* async jobs. Value = the meshId:nodeId job key holding the lease (for diagnostics).
|
|
63324
|
+
*/
|
|
63325
|
+
refineBaseLeases = /* @__PURE__ */ new Map();
|
|
62844
63326
|
constructor(deps) {
|
|
62845
63327
|
this.deps = deps;
|
|
62846
63328
|
}
|