@adhdev/daemon-standalone 1.0.56-rc.3 → 1.0.56-rc.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35904,6 +35904,15 @@ var require_dist3 = __commonJS({
35904
35904
  } else {
35905
35905
  delete policy.quotaRouting;
35906
35906
  }
35907
+ if (policy.onDependencyFailure === void 0 || policy.onDependencyFailure === "block") {
35908
+ delete policy.onDependencyFailure;
35909
+ } else if (policy.onDependencyFailure === "cancel") {
35910
+ policy.onDependencyFailure = "cancel";
35911
+ } else {
35912
+ throw new Error(
35913
+ `invalid_on_dependency_failure: must be 'block' or 'cancel' (got ${JSON.stringify(policy.onDependencyFailure)}). Invalid values are rejected; they do not silently become 'block'.`
35914
+ );
35915
+ }
35907
35916
  return policy;
35908
35917
  }
35909
35918
  function resolveDelegatedWorkerAutoApprove(meshPolicy, nodePolicy, provider, repoConfig, providerType, overrideModeId, overrideLegacyAutoApprove) {
@@ -36040,7 +36049,8 @@ var require_dist3 = __commonJS({
36040
36049
  // notifies the owner. Opt into 'auto_silent_on_dispatch' to one-shot-silence the
36041
36050
  // routine idle push for a coordinator-driven task (approval/failure notifications
36042
36051
  // are never affected — see coordinatorIdlePushPolicy).
36043
- coordinatorIdlePushPolicy: "always"
36052
+ coordinatorIdlePushPolicy: "always",
36053
+ onDependencyFailure: "block"
36044
36054
  };
36045
36055
  DEFAULT_QUOTA_ROUTING_POLICY = {
36046
36056
  staleAfterMs: 30 * 60 * 1e3,
@@ -36610,10 +36620,10 @@ var require_dist3 = __commonJS({
36610
36620
  }
36611
36621
  function getDaemonBuildInfo() {
36612
36622
  if (cached2) return cached2;
36613
- const commit = readInjected(true ? "043f842c15bd9babf19ea49b2392bf827f8cc97a" : void 0) ?? "unknown";
36614
- const commitShort = readInjected(true ? "043f842c" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36615
- const version2 = readInjected(true ? "1.0.56-rc.3" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36616
- const builtAt = readInjected(true ? "2026-08-18T13:29:56.354Z" : void 0);
36623
+ const commit = readInjected(true ? "d41089a0d560d75e670a90d3c4e9955bf955bbbc" : void 0) ?? "unknown";
36624
+ const commitShort = readInjected(true ? "d41089a0" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36625
+ const version2 = readInjected(true ? "1.0.56-rc.5" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36626
+ const builtAt = readInjected(true ? "2026-08-19T05:42:19.473Z" : void 0);
36617
36627
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36618
36628
  return cached2;
36619
36629
  }
@@ -42842,9 +42852,14 @@ ${error48.message || ""}`;
42842
42852
  CANONICAL_MESH_TOOL_NAMES = [
42843
42853
  "mesh_status",
42844
42854
  "mesh_list_nodes",
42845
- "mesh_enqueue_task",
42855
+ // GRAPH-ORCHESTRATION Phase F — batch before task, mirroring ALL_MESH_TOOLS.
42846
42856
  "mesh_enqueue_batch",
42857
+ "mesh_enqueue_task",
42847
42858
  "mesh_view_queue",
42859
+ // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
42860
+ "mesh_graph_view",
42861
+ "mesh_graph_gate_claim",
42862
+ "mesh_graph_gate_release",
42848
42863
  "mesh_queue_cancel",
42849
42864
  "mesh_queue_requeue",
42850
42865
  "mesh_send_task",
@@ -43952,6 +43967,10 @@ ${error48.message || ""}`;
43952
43967
  return `[System] ${args.nodeLabel} already has completion evidence${metadata}. The no-progress monitor reconciled the terminal handoff and marked the session complete; wait for the queued completion event/status refresh before doing any manual transcript check.`;
43953
43968
  }
43954
43969
  const reviewRecommended = args.metadataEvent.reviewRecommended === true;
43970
+ const timedOutNoResponse = args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" && args.metadataEvent.completionDiagnostic.timedOutWithoutFinalAssistant === true;
43971
+ if (timedOutNoResponse) {
43972
+ return `[System] ${args.nodeLabel} was forcibly terminated WITHOUT a response${metadata}: the finalization wait expired with no final assistant answer, so the task was recorded as a forced termination (failed), NOT as a completion. Verify the actual state with mesh_read_chat / git status and re-dispatch the task if the work is still needed.`;
43973
+ }
43955
43974
  const weakCompletion = isWeakCompletionMetadata(args.metadataEvent);
43956
43975
  const completionLead = weakCompletion ? `[System] ${args.nodeLabel} reported a possible completion (weak evidence) \u2014 awaiting confirmation${metadata}.` : `[System] ${args.nodeLabel} has completed its task and is now idle${metadata}.`;
43957
43976
  const verifyTextNote = " Completion evidence is weak \u2014 verify via mesh_read_chat or git status before declaring the task done; the worker may still be mid-turn or parked on an approval/modal.";
@@ -45352,14 +45371,40 @@ Next step: ${nextStep}`;
45352
45371
  sessionOrderChains = /* @__PURE__ */ new Map();
45353
45372
  }
45354
45373
  });
45374
+ function newMeshGraphId() {
45375
+ return (0, import_crypto6.randomUUID)();
45376
+ }
45377
+ function newMeshGraphNodeId() {
45378
+ return (0, import_crypto6.randomUUID)();
45379
+ }
45380
+ function newMeshGraphGateId() {
45381
+ return (0, import_crypto6.randomUUID)();
45382
+ }
45355
45383
  function newMeshGraphOutboxId() {
45356
45384
  return (0, import_crypto6.randomUUID)();
45357
45385
  }
45358
45386
  var import_crypto6;
45387
+ var MESH_GRAPH_SCHEMA_VERSION;
45388
+ var MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES;
45389
+ var MESH_GRAPH_GATE_ACTIONS;
45390
+ var MESH_GRAPH_GATE_TIMEOUT_POLICIES;
45391
+ var MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES;
45359
45392
  var init_mesh_graph_types = __esm2({
45360
45393
  "src/mesh/mesh-graph-types.ts"() {
45361
45394
  "use strict";
45362
45395
  import_crypto6 = require("crypto");
45396
+ MESH_GRAPH_SCHEMA_VERSION = 2;
45397
+ MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES = ["skip", "omit_dependency"];
45398
+ MESH_GRAPH_GATE_ACTIONS = [
45399
+ "refinery",
45400
+ "approval",
45401
+ "ci_wait",
45402
+ "publish",
45403
+ "deploy",
45404
+ "custom"
45405
+ ];
45406
+ MESH_GRAPH_GATE_TIMEOUT_POLICIES = ["hold", "cancel_downstream", "fail_graph"];
45407
+ MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES = ["block", "cancel"];
45363
45408
  }
45364
45409
  });
45365
45410
  function maskKeepTail(token) {
@@ -45895,6 +45940,188 @@ ${blocks.join("\n\n")}`;
45895
45940
  init_mesh_graph_workspace_identity();
45896
45941
  }
45897
45942
  });
45943
+ function parseOnDependencyFailurePolicy(value) {
45944
+ if (value === void 0 || value === null || value === "") return "block";
45945
+ if (typeof value === "string" && MESH_GRAPH_ON_DEPENDENCY_FAILURE_POLICIES.includes(value)) {
45946
+ return value;
45947
+ }
45948
+ throw new MeshGraphPolicyError(
45949
+ `invalid_on_dependency_failure: must be 'block' or 'cancel' (got ${JSON.stringify(value)}). Invalid values are rejected; they do not silently become 'block'.`
45950
+ );
45951
+ }
45952
+ function resolveOnDependencyFailurePolicy(value) {
45953
+ return value === "cancel" ? "cancel" : "block";
45954
+ }
45955
+ function parseGraphPolicy(policyJson) {
45956
+ const parsed = safeParseObject(policyJson);
45957
+ const raw = parsed?.on_dependency_failure ?? parsed?.onDependencyFailure;
45958
+ return { on_dependency_failure: parseOnDependencyFailurePolicy(raw) };
45959
+ }
45960
+ function projectGraphPublicPolicy(policyJson) {
45961
+ try {
45962
+ return parseGraphPolicy(policyJson);
45963
+ } catch (e) {
45964
+ return {
45965
+ on_dependency_failure: "block",
45966
+ policyInvalid: e instanceof Error ? e.message : String(e)
45967
+ };
45968
+ }
45969
+ }
45970
+ function serializeGraphPolicy(policy) {
45971
+ return JSON.stringify({ on_dependency_failure: policy.on_dependency_failure });
45972
+ }
45973
+ function isGraphSkipPlaceholder(entry) {
45974
+ if (!entry) return false;
45975
+ const reason = entry.blockedReason ?? entry.cancelReason ?? "";
45976
+ return entry.status === "cancelled" && reason.startsWith(MESH_GRAPH_SKIP_BLOCK_PREFIX);
45977
+ }
45978
+ function parseLegacyDependencyFailedPredecessor(reason) {
45979
+ if (typeof reason !== "string") return null;
45980
+ if (!reason.startsWith(LEGACY_DEPENDENCY_FAILED_PREFIX)) return null;
45981
+ const rest = reason.slice(LEGACY_DEPENDENCY_FAILED_PREFIX.length);
45982
+ if (!rest || rest.startsWith(":")) return null;
45983
+ return rest;
45984
+ }
45985
+ function deriveDependencyFailures(dependsOn, statusById, depMetaById) {
45986
+ const deps = Array.isArray(dependsOn) ? dependsOn : [];
45987
+ const failures = [];
45988
+ for (const taskId of deps) {
45989
+ const meta3 = depMetaById?.get(taskId);
45990
+ const status = statusById.get(taskId) ?? meta3?.status;
45991
+ if (isGraphSkipPlaceholder({ status, blockedReason: meta3?.blockedReason, cancelReason: meta3?.cancelReason })) {
45992
+ continue;
45993
+ }
45994
+ if (status !== "failed" && status !== "cancelled") continue;
45995
+ const reason = meta3?.cancelReason ?? meta3?.blockedReason;
45996
+ failures.push({
45997
+ taskId,
45998
+ status,
45999
+ ...reason ? { reason } : {}
46000
+ });
46001
+ }
46002
+ return failures;
46003
+ }
46004
+ function classifyGraphRollup(nodes) {
46005
+ const gates = nodes.filter((n) => n.kind === "coordinator_gate");
46006
+ const workers = nodes.filter((n) => n.kind !== "coordinator_gate");
46007
+ if (gates.some((g) => !GATE_SETTLED.has(g.state))) return null;
46008
+ if (workers.some((w) => !WORKER_SETTLED.has(w.state))) return null;
46009
+ if (workers.some((w) => w.state === "failed")) return "failed";
46010
+ if (workers.some((w) => w.state === "cancelled") || gates.some((g) => g.state === "cancelled" || g.state === "expired")) {
46011
+ return "cancelled";
46012
+ }
46013
+ if (workers.length === 0 && gates.length === 0) return "completed";
46014
+ if (workers.every((w) => SUCCESS_TERMINAL.has(w.state)) && gates.every((g) => g.state === "released" || SUCCESS_TERMINAL.has(g.state))) {
46015
+ return "completed";
46016
+ }
46017
+ return "completed";
46018
+ }
46019
+ function applyGraphCancelCascade(store, nodes, edges, origin, nowIso, updateNodeState) {
46020
+ const byId = new Map(nodes.map((n) => [n.nodeId, n]));
46021
+ const frontier = [origin.nodeId];
46022
+ const seen = new Set(frontier);
46023
+ const cancelledNodeIds = [];
46024
+ const cancelledTaskIds = [];
46025
+ const sourceTaskId = origin.queueTaskId ?? origin.nodeId;
46026
+ const sourceRef = origin.ref ?? origin.nodeId;
46027
+ while (frontier.length > 0) {
46028
+ const currentId = frontier.pop();
46029
+ const outgoing = edges.filter((e) => e.fromNodeId === currentId && e.kind !== "gate");
46030
+ for (const edge of outgoing) {
46031
+ const target = byId.get(edge.toNodeId);
46032
+ if (!target || seen.has(target.nodeId)) continue;
46033
+ seen.add(target.nodeId);
46034
+ if (target.kind !== "worker_task") continue;
46035
+ if (target.state === "skipped") continue;
46036
+ if (!CANCELABLE_NODE_STATES.has(target.state)) continue;
46037
+ const reason = `${LEGACY_DEPENDENCY_FAILED_PREFIX}${sourceRef}`;
46038
+ updateNodeState(target, reason);
46039
+ target.state = "cancelled";
46040
+ target.failureReason = reason;
46041
+ cancelledNodeIds.push(target.nodeId);
46042
+ if (target.queueTaskId) {
46043
+ const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
46044
+ if (entry && entry.status === "pending") {
46045
+ cancelledTaskIds.push(entry.id);
46046
+ }
46047
+ }
46048
+ frontier.push(target.nodeId);
46049
+ }
46050
+ }
46051
+ return { cancelledNodeIds, cancelledTaskIds };
46052
+ }
46053
+ function migrateLegacyDependencyFailedQueueBlocks(db) {
46054
+ let rows;
46055
+ try {
46056
+ rows = db.prepare(`SELECT id, mesh_id, payload FROM mesh_queue`).all();
46057
+ } catch {
46058
+ return [];
46059
+ }
46060
+ const cleared = [];
46061
+ const update = db.prepare(`UPDATE mesh_queue SET payload = ?, updated_at = ? WHERE id = ? AND mesh_id = ?`);
46062
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
46063
+ for (const row of rows) {
46064
+ let entry;
46065
+ try {
46066
+ entry = JSON.parse(row.payload);
46067
+ } catch {
46068
+ continue;
46069
+ }
46070
+ const predecessorId = parseLegacyDependencyFailedPredecessor(entry.blockedReason);
46071
+ if (!predecessorId) continue;
46072
+ delete entry.blockedReason;
46073
+ update.run(JSON.stringify(entry), nowIso, row.id, row.mesh_id);
46074
+ cleared.push({ taskId: row.id, meshId: row.mesh_id, predecessorId });
46075
+ }
46076
+ return cleared;
46077
+ }
46078
+ function safeParseObject(text) {
46079
+ if (!text) return null;
46080
+ try {
46081
+ const value = JSON.parse(text);
46082
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
46083
+ return value;
46084
+ } catch {
46085
+ return null;
46086
+ }
46087
+ }
46088
+ var MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT;
46089
+ var MESH_GRAPH_SKIP_BLOCK_PREFIX;
46090
+ var LEGACY_DEPENDENCY_FAILED_PREFIX;
46091
+ var MeshGraphPolicyError;
46092
+ var SUCCESS_TERMINAL;
46093
+ var WORKER_SETTLED;
46094
+ var GATE_SETTLED;
46095
+ var CANCELABLE_NODE_STATES;
46096
+ var init_mesh_graph_derived_failure = __esm2({
46097
+ "src/mesh/mesh-graph-derived-failure.ts"() {
46098
+ "use strict";
46099
+ init_mesh_graph_types();
46100
+ MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT = "on_dependency_failure controls downstream tasks when a required worker task fails or is cancelled. `block` (default) keeps downstream pending and automatically recovers if the predecessor is retried and later completes. `cancel` terminally cancels the dependent branch; it is not revived by predecessor retry.";
46101
+ MESH_GRAPH_SKIP_BLOCK_PREFIX = "graph_skipped:";
46102
+ LEGACY_DEPENDENCY_FAILED_PREFIX = "dependency_failed:";
46103
+ MeshGraphPolicyError = class extends Error {
46104
+ constructor(message, code = "invalid_on_dependency_failure") {
46105
+ super(message);
46106
+ this.code = code;
46107
+ this.name = "MeshGraphPolicyError";
46108
+ }
46109
+ };
46110
+ SUCCESS_TERMINAL = /* @__PURE__ */ new Set(["completed", "released", "skipped"]);
46111
+ WORKER_SETTLED = /* @__PURE__ */ new Set([
46112
+ "completed",
46113
+ "skipped",
46114
+ "failed",
46115
+ "cancelled"
46116
+ ]);
46117
+ GATE_SETTLED = /* @__PURE__ */ new Set(["released", "cancelled", "expired"]);
46118
+ CANCELABLE_NODE_STATES = /* @__PURE__ */ new Set([
46119
+ "declared",
46120
+ "blocked",
46121
+ "materialized"
46122
+ ]);
46123
+ }
46124
+ });
45898
46125
  function graphMaterializationBlockReason(nodeId, materializationVersion) {
45899
46126
  return `${GRAPH_BLOCK_PREFIX}${nodeId}:${materializationVersion}`;
45900
46127
  }
@@ -45907,6 +46134,14 @@ ${blocks.join("\n\n")}`;
45907
46134
  if (!Number.isInteger(version2) || version2 < 0) return null;
45908
46135
  return { nodeId: rest.slice(0, sep15), version: version2 };
45909
46136
  }
46137
+ function coordinatorGateBlockReason(gateId) {
46138
+ return `${GATE_BLOCK_PREFIX}${gateId}`;
46139
+ }
46140
+ function parseCoordinatorGateBlock(reason) {
46141
+ if (!reason || !reason.startsWith(GATE_BLOCK_PREFIX)) return null;
46142
+ const gateId = reason.slice(GATE_BLOCK_PREFIX.length);
46143
+ return gateId.length > 0 ? { gateId } : null;
46144
+ }
45910
46145
  function registerMeshGraphQueueWakeHandler(handler) {
45911
46146
  queueWakeHandler = handler;
45912
46147
  }
@@ -46030,11 +46265,59 @@ ${blocks.join("\n\n")}`;
46030
46265
  });
46031
46266
  const nodes = graphStore.listNodes(node.graphId);
46032
46267
  const byId = new Map(nodes.map((n) => [n.nodeId, n]));
46033
- let graphRolledUp = false;
46034
- if (nodes.every(isTerminalEquivalent)) {
46035
- graphStore.updateGraphStatus(node.graphId, "completed", nowIso, true);
46036
- outbox.push({ kind: "graph_completed", payload: { graphId: node.graphId } });
46037
- graphRolledUp = true;
46268
+ const graph = graphStore.getGraph(node.graphId);
46269
+ const failurePolicy = projectGraphPublicPolicy(graph?.policyJson).on_dependency_failure;
46270
+ if ((terminal.status === "failed" || terminal.status === "cancelled") && failurePolicy === "cancel") {
46271
+ const edges = graphStore.listEdges(node.graphId);
46272
+ const cascade = applyGraphCancelCascade(
46273
+ store,
46274
+ nodes,
46275
+ edges,
46276
+ node,
46277
+ nowIso,
46278
+ (target, reason) => {
46279
+ graphStore.updateNodeState(target.graphId, target.nodeId, "cancelled", nowIso, {
46280
+ failureReason: reason
46281
+ });
46282
+ }
46283
+ );
46284
+ for (const taskId of cascade.cancelledTaskIds) {
46285
+ const entry = store.findQueueEntryById(node.meshId, taskId);
46286
+ if (entry && entry.status === "pending") {
46287
+ entry.status = "cancelled";
46288
+ entry.cancelledAt = nowIso;
46289
+ entry.cancelReason = `dependency_failed:${terminal.taskId}`;
46290
+ store.updateQueueEntry(entry);
46291
+ }
46292
+ }
46293
+ for (const nodeId of cascade.cancelledNodeIds) {
46294
+ const target = byId.get(nodeId);
46295
+ outbox.push({
46296
+ kind: "graph_node_cancelled",
46297
+ payload: {
46298
+ graphId: node.graphId,
46299
+ nodeId,
46300
+ ref: target?.ref,
46301
+ taskId: target?.queueTaskId,
46302
+ reason: `dependency_failed:${node.ref ?? node.nodeId}`
46303
+ }
46304
+ });
46305
+ }
46306
+ } else if (terminal.status === "failed" || terminal.status === "cancelled") {
46307
+ const dependents = graphStore.listEdges(node.graphId).filter((e) => e.fromNodeId === node.nodeId && e.kind !== "gate").map((e) => byId.get(e.toNodeId)).filter((n) => !!n && n.kind === "worker_task");
46308
+ outbox.push({
46309
+ kind: "graph_derived_failure",
46310
+ payload: {
46311
+ graphId: node.graphId,
46312
+ nodeId: node.nodeId,
46313
+ ref: node.ref,
46314
+ taskId: terminal.taskId,
46315
+ outcome: terminal.status,
46316
+ policy: "block",
46317
+ dependentNodeIds: dependents.map((d) => d.nodeId),
46318
+ dependentTaskIds: dependents.map((d) => d.queueTaskId).filter(Boolean)
46319
+ }
46320
+ });
46038
46321
  }
46039
46322
  const materialized = [];
46040
46323
  if (terminal.status === "completed") {
@@ -46047,7 +46330,12 @@ ${blocks.join("\n\n")}`;
46047
46330
  );
46048
46331
  for (const targetId of downstreamIds) {
46049
46332
  const target = byId.get(targetId);
46050
- if (!target || target.kind !== "worker_task" || !target.queueTaskId) continue;
46333
+ if (!target) continue;
46334
+ if (target.kind === "coordinator_gate") {
46335
+ maybeOpenCoordinatorGate(store, target, edges, byId, nowIso);
46336
+ continue;
46337
+ }
46338
+ if (target.kind !== "worker_task" || !target.queueTaskId) continue;
46051
46339
  if (target.state !== "declared" && target.state !== "blocked") continue;
46052
46340
  const outcome = settleDownstreamNode(store, target, edges, byId, nowIso);
46053
46341
  if (outcome.kind === "materialized") {
@@ -46091,10 +46379,12 @@ ${blocks.join("\n\n")}`;
46091
46379
  }
46092
46380
  frontier = nextFrontier;
46093
46381
  }
46094
- if (!graphRolledUp && graphStore.listNodes(node.graphId).every(isTerminalEquivalent)) {
46095
- graphStore.updateGraphStatus(node.graphId, "completed", nowIso, true);
46096
- outbox.push({ kind: "graph_completed", payload: { graphId: node.graphId } });
46097
- }
46382
+ }
46383
+ const rolled = classifyGraphRollup(graphStore.listNodes(node.graphId));
46384
+ if (rolled && graph?.status !== rolled) {
46385
+ graphStore.updateGraphStatus(node.graphId, rolled, nowIso, true);
46386
+ const kind = rolled === "completed" ? "graph_completed" : rolled === "failed" ? "graph_failed" : "graph_cancelled";
46387
+ outbox.push({ kind, payload: { graphId: node.graphId, status: rolled } });
46098
46388
  }
46099
46389
  if (materialized.length > 0) {
46100
46390
  outbox.push({ kind: "queue_wake", payload: { meshId: node.meshId, reason: "graph_materialization", graphId: node.graphId } });
@@ -46117,9 +46407,95 @@ ${blocks.join("\n\n")}`;
46117
46407
  function isTerminalEquivalent(n) {
46118
46408
  return n.state === "completed" || n.state === "released" || n.state === "skipped";
46119
46409
  }
46410
+ function maybeOpenCoordinatorGate(store, gateNode, edges, byId, nowIso) {
46411
+ const graphStore = store.graphStore();
46412
+ if (gateNode.state !== "declared" && gateNode.state !== "blocked") return false;
46413
+ const gate = graphStore.findGateByNodeId(gateNode.graphId, gateNode.nodeId);
46414
+ if (!gate || gate.state !== "declared") return false;
46415
+ const incoming = edges.filter((e) => e.toNodeId === gateNode.nodeId);
46416
+ const satisfied = incoming.every((e) => {
46417
+ const sourceState = byId.get(e.fromNodeId)?.state;
46418
+ return sourceState === "completed" || sourceState === "released" || sourceState === "skipped" && e.omitOnSkip;
46419
+ });
46420
+ if (!satisfied) return false;
46421
+ const spec = safeParseJson(gateNode.baseSpecJson);
46422
+ const deadlineSeconds = typeof spec?.deadline_seconds === "number" ? spec.deadline_seconds : void 0;
46423
+ const deadlineAt = deadlineSeconds && deadlineSeconds > 0 ? new Date(Date.parse(nowIso) + deadlineSeconds * 1e3).toISOString() : void 0;
46424
+ graphStore.patchGate(gate.gateId, {
46425
+ state: "awaiting_coordinator",
46426
+ ...deadlineAt ? { deadlineAt } : {}
46427
+ }, nowIso);
46428
+ graphStore.updateNodeState(gateNode.graphId, gateNode.nodeId, "awaiting_coordinator", nowIso);
46429
+ gateNode.state = "awaiting_coordinator";
46430
+ byId.set(gateNode.nodeId, gateNode);
46431
+ const block2 = coordinatorGateBlockReason(gate.gateId);
46432
+ for (const edge of edges.filter((e) => e.fromNodeId === gateNode.nodeId)) {
46433
+ const target = byId.get(edge.toNodeId);
46434
+ if (!target || target.kind !== "worker_task" || !target.queueTaskId) continue;
46435
+ if (target.state !== "declared" && target.state !== "blocked") continue;
46436
+ if (target.state !== "blocked") {
46437
+ graphStore.updateNodeState(target.graphId, target.nodeId, "blocked", nowIso);
46438
+ target.state = "blocked";
46439
+ byId.set(target.nodeId, target);
46440
+ }
46441
+ const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
46442
+ if (!entry || entry.status !== "pending") continue;
46443
+ const owned = !entry.blockedReason || parseGraphMaterializationBlock(entry.blockedReason) !== null || parseCoordinatorGateBlock(entry.blockedReason) !== null || entry.blockedReason.startsWith("materialization_error:");
46444
+ if (owned && entry.blockedReason !== block2) {
46445
+ entry.blockedReason = block2;
46446
+ store.updateQueueEntry(entry);
46447
+ }
46448
+ }
46449
+ graphStore.insertOutboxEvent({
46450
+ id: newMeshGraphOutboxId(),
46451
+ meshId: gateNode.meshId,
46452
+ graphId: gateNode.graphId,
46453
+ kind: "graph_gate_awaiting",
46454
+ payload: JSON.stringify({
46455
+ graphId: gateNode.graphId,
46456
+ gateId: gate.gateId,
46457
+ nodeId: gateNode.nodeId,
46458
+ ref: gate.ref ?? gateNode.ref,
46459
+ action: gate.action,
46460
+ instructions: gate.instructions,
46461
+ deadlineAt
46462
+ }),
46463
+ status: "pending",
46464
+ attemptCount: 0,
46465
+ createdAt: nowIso,
46466
+ updatedAt: nowIso
46467
+ });
46468
+ const graph = graphStore.getGraph(gateNode.graphId);
46469
+ if (graph?.status === "active") {
46470
+ graphStore.updateGraphStatus(gateNode.graphId, "waiting_gate", nowIso);
46471
+ }
46472
+ return true;
46473
+ }
46474
+ function gateOutcomeOutputForNode(graphStore, gateNode) {
46475
+ const gate = graphStore.findGateByNodeId(gateNode.graphId, gateNode.nodeId);
46476
+ if (!gate || gate.state !== "released") return null;
46477
+ const stored = typeof gate.releaseEvidenceJson === "string" ? safeParseJson(gate.releaseEvidenceJson) : void 0;
46478
+ return {
46479
+ ref: gateNode.ref ?? gate.ref ?? gateNode.nodeId,
46480
+ envelope: {
46481
+ gate_outcome: gate.releaseOutcome,
46482
+ action: gate.action,
46483
+ gate_id: gate.gateId,
46484
+ ref: gate.ref ?? gateNode.ref,
46485
+ result: stored?.result,
46486
+ evidence: stored?.evidence,
46487
+ evidence_digest: gate.releaseEvidenceDigest,
46488
+ released_at: gate.updatedAt
46489
+ }
46490
+ };
46491
+ }
46120
46492
  function settleDownstreamNode(store, target, edges, byId, nowIso) {
46121
46493
  const graphStore = store.graphStore();
46122
46494
  const incoming = edges.filter((e) => e.toNodeId === target.nodeId && e.kind !== "gate");
46495
+ const gateIncoming = edges.filter((e) => e.toNodeId === target.nodeId && e.kind === "gate");
46496
+ for (const edge of gateIncoming) {
46497
+ if (byId.get(edge.fromNodeId)?.state !== "released") return { kind: "deferred" };
46498
+ }
46123
46499
  const skippedSources = incoming.filter((e) => byId.get(e.fromNodeId)?.state === "skipped");
46124
46500
  const propagatingSkip = skippedSources.find((e) => !e.omitOnSkip);
46125
46501
  if (propagatingSkip) {
@@ -46132,6 +46508,12 @@ ${blocks.join("\n\n")}`;
46132
46508
  const queueEntry = store.findQueueEntryById(target.meshId, target.queueTaskId);
46133
46509
  if (!queueEntry) return { kind: "deferred" };
46134
46510
  const outputsByRef = collectUpstreamOutputs(store, activeIncoming, byId);
46511
+ for (const edge of gateIncoming) {
46512
+ const gateNode = byId.get(edge.fromNodeId);
46513
+ if (!gateNode) continue;
46514
+ const output = gateOutcomeOutputForNode(graphStore, gateNode);
46515
+ if (output && !outputsByRef.has(output.ref)) outputsByRef.set(output.ref, output);
46516
+ }
46135
46517
  const baseSpec = safeParseJson(target.baseSpecJson);
46136
46518
  let conditionsHold = true;
46137
46519
  let falseCondition;
@@ -46287,6 +46669,10 @@ ${blocks.join("\n\n")}`;
46287
46669
  }
46288
46670
  if (queueEntry.blockedReason?.startsWith("materialization_error:")) {
46289
46671
  delete queueEntry.blockedReason;
46672
+ return;
46673
+ }
46674
+ if (parseCoordinatorGateBlock(queueEntry.blockedReason)) {
46675
+ delete queueEntry.blockedReason;
46290
46676
  }
46291
46677
  }
46292
46678
  function syncNodeRowState(byId, target, state, patch) {
@@ -46375,6 +46761,7 @@ ${blocks.join("\n\n")}`;
46375
46761
  return result.materialized;
46376
46762
  }
46377
46763
  var GRAPH_BLOCK_PREFIX;
46764
+ var GATE_BLOCK_PREFIX;
46378
46765
  var queueWakeHandler;
46379
46766
  var init_mesh_graph_transition_runner = __esm2({
46380
46767
  "src/mesh/mesh-graph-transition-runner.ts"() {
@@ -46386,7 +46773,9 @@ ${blocks.join("\n\n")}`;
46386
46773
  init_mesh_graph_types();
46387
46774
  init_mesh_graph_input_binding();
46388
46775
  init_mesh_graph_workspace_bind();
46776
+ init_mesh_graph_derived_failure();
46389
46777
  GRAPH_BLOCK_PREFIX = "graph_materialization_pending:";
46778
+ GATE_BLOCK_PREFIX = "coordinator_gate:";
46390
46779
  }
46391
46780
  });
46392
46781
  function formatMeshTaskModeViolations(result) {
@@ -48212,7 +48601,10 @@ ${blocks.join("\n\n")}`;
48212
48601
  return record2 ? { ...record2, status: normalizeMissionStatus(record2.status), source: normalizeMissionSource(record2.source) } : null;
48213
48602
  }
48214
48603
  function summarizeMissionTasks(meshId, missionId) {
48215
- const tasks = getQueue(meshId).filter((task) => task.missionId === missionId);
48604
+ const queue = getQueue(meshId);
48605
+ const tasks = queue.filter((task) => task.missionId === missionId);
48606
+ const statusById = new Map(queue.map((task) => [task.id, task.status]));
48607
+ const depMetaById = new Map(queue.map((task) => [task.id, { blockedReason: task.blockedReason, cancelReason: task.cancelReason, status: task.status }]));
48216
48608
  const aggregate = {
48217
48609
  total: tasks.length,
48218
48610
  pending: 0,
@@ -48229,7 +48621,9 @@ ${blocks.join("\n\n")}`;
48229
48621
  else if (task.status === "completed") aggregate.completed += 1;
48230
48622
  else if (task.status === "failed") aggregate.failed += 1;
48231
48623
  else if (task.status === "cancelled") aggregate.cancelled += 1;
48232
- if (task.status === "pending" && task.blockedReason) aggregate.blocked += 1;
48624
+ if (task.status === "pending" && (task.blockedReason || deriveDependencyFailures(task.dependsOn, statusById, depMetaById).length > 0)) {
48625
+ aggregate.blocked += 1;
48626
+ }
48233
48627
  if (task.updatedAt && (!aggregate.lastActivityAt || task.updatedAt > aggregate.lastActivityAt)) {
48234
48628
  aggregate.lastActivityAt = task.updatedAt;
48235
48629
  }
@@ -48425,6 +48819,7 @@ ${blocks.join("\n\n")}`;
48425
48819
  init_logger();
48426
48820
  init_mesh_runtime_store();
48427
48821
  init_mesh_work_queue();
48822
+ init_mesh_graph_derived_failure();
48428
48823
  init_mesh_task_stats();
48429
48824
  init_mesh_ledger();
48430
48825
  init_mesh_events_pending();
@@ -48857,13 +49252,14 @@ ${blocks.join("\n\n")}`;
48857
49252
  function resolveDependencyFailurePolicy(meshId) {
48858
49253
  try {
48859
49254
  const policy = getMesh(meshId)?.policy ?? {};
48860
- return policy.onDependencyFailure === "cancel" ? "cancel" : "block";
49255
+ return resolveOnDependencyFailurePolicy(policy.onDependencyFailure ?? policy.on_dependency_failure);
48861
49256
  } catch {
48862
49257
  return "block";
48863
49258
  }
48864
49259
  }
48865
49260
  function propagateDependencyFailure(meshId, failedTaskId) {
48866
49261
  const policy = resolveDependencyFailurePolicy(meshId);
49262
+ if (policy !== "cancel") return [];
48867
49263
  const store = MeshRuntimeStore.getInstance();
48868
49264
  const cancelled = [];
48869
49265
  const frontier = [failedTaskId];
@@ -48874,17 +49270,12 @@ ${blocks.join("\n\n")}`;
48874
49270
  for (const dependent of dependents) {
48875
49271
  if (seen.has(dependent.id)) continue;
48876
49272
  seen.add(dependent.id);
48877
- if (policy === "cancel") {
48878
- dependent.status = "cancelled";
48879
- dependent.cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
48880
- dependent.cancelReason = `dependency_failed:${currentId}`;
48881
- store.updateQueueEntry(dependent);
48882
- cancelled.push(dependent);
48883
- frontier.push(dependent.id);
48884
- } else {
48885
- dependent.blockedReason = `dependency_failed:${currentId}`;
48886
- store.updateQueueEntry(dependent);
48887
- }
49273
+ dependent.status = "cancelled";
49274
+ dependent.cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
49275
+ dependent.cancelReason = `dependency_failed:${currentId}`;
49276
+ store.updateQueueEntry(dependent);
49277
+ cancelled.push(dependent);
49278
+ frontier.push(dependent.id);
48888
49279
  }
48889
49280
  }
48890
49281
  return cancelled;
@@ -49180,12 +49571,13 @@ ${blocks.join("\n\n")}`;
49180
49571
  const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
49181
49572
  return deps.every((depId) => statusById.get(depId) === "completed");
49182
49573
  }
49183
- function describeTaskDependencyState(entry, statusById) {
49574
+ function describeTaskDependencyState(entry, statusById, depMetaById) {
49184
49575
  const deps = Array.isArray(entry.dependsOn) ? entry.dependsOn : [];
49185
49576
  const waitingOn = deps.filter((depId) => statusById.get(depId) !== "completed");
49186
49577
  return {
49187
49578
  waitingOn,
49188
- dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById)
49579
+ dependenciesSatisfied: taskDependenciesSatisfied(entry, statusById),
49580
+ dependencyFailures: deriveDependencyFailures(entry.dependsOn, statusById, depMetaById)
49189
49581
  };
49190
49582
  }
49191
49583
  function getMeshQueueStats(meshId) {
@@ -49305,6 +49697,7 @@ ${blocks.join("\n\n")}`;
49305
49697
  init_mesh_task_inflight();
49306
49698
  init_mesh_turn_ledger();
49307
49699
  init_mesh_graph_transition_runner();
49700
+ init_mesh_graph_derived_failure();
49308
49701
  init_dist();
49309
49702
  init_mesh_task_mode_guardrail();
49310
49703
  init_mesh_task_mode_guardrail();
@@ -49520,10 +49913,12 @@ ${blocks.join("\n\n")}`;
49520
49913
  CREATE INDEX IF NOT EXISTS idx_mesh_graph_outbox_graph
49521
49914
  ON mesh_graph_outbox(graph_id, status);
49522
49915
  `);
49916
+ migrateLegacyDependencyFailedQueueBlocks(db);
49523
49917
  }
49524
49918
  var init_mesh_graph_schema = __esm2({
49525
49919
  "src/mesh/mesh-graph-schema.ts"() {
49526
49920
  "use strict";
49921
+ init_mesh_graph_derived_failure();
49527
49922
  }
49528
49923
  });
49529
49924
  function mapGraphRow(r) {
@@ -49704,6 +50099,39 @@ ${blocks.join("\n\n")}`;
49704
50099
  ).get(meshId, batchId);
49705
50100
  return r ? mapGraphRow(r) : null;
49706
50101
  }
50102
+ /**
50103
+ * Phase-E graph listing for the graph view / metrics (design :759-775). Newest
50104
+ * first so a view without an explicit graph id shows current work. `statuses`
50105
+ * narrows to e.g. the in-flight set.
50106
+ */
50107
+ listGraphsByMesh(meshId, opts) {
50108
+ const limit = Math.max(1, Math.min(opts?.limit ?? 50, 500));
50109
+ const statuses = opts?.statuses;
50110
+ if (statuses && statuses.length > 0) {
50111
+ const placeholders = statuses.map(() => "?").join(",");
50112
+ const rows2 = this.db.prepare(
50113
+ `SELECT * FROM mesh_task_graphs WHERE mesh_id = ? AND status IN (${placeholders})
50114
+ ORDER BY created_at DESC LIMIT ?`
50115
+ ).all(meshId, ...statuses, limit);
50116
+ return rows2.map(mapGraphRow);
50117
+ }
50118
+ const rows = this.db.prepare(
50119
+ `SELECT * FROM mesh_task_graphs WHERE mesh_id = ? ORDER BY created_at DESC LIMIT ?`
50120
+ ).all(meshId, limit);
50121
+ return rows.map(mapGraphRow);
50122
+ }
50123
+ /**
50124
+ * Phase-E: replace the graph's normalized policy document. Used at plan-commit
50125
+ * time to attach the enqueue-decision provenance record next to the persisted
50126
+ * `on_dependency_failure` (design :697-710, :733-738). Deliberately a whole-
50127
+ * document write — the policy is normalized once at commit and never patched
50128
+ * field-by-field afterwards.
50129
+ */
50130
+ updateGraphPolicyJson(graphId, policyJson, nowIso) {
50131
+ this.db.prepare(
50132
+ `UPDATE mesh_task_graphs SET policy_json = ?, updated_at = ? WHERE graph_id = ?`
50133
+ ).run(policyJson, nowIso, graphId);
50134
+ }
49707
50135
  /** Phase-B rollup: graph status transition once every node reached a terminal-equivalent state. */
49708
50136
  updateGraphStatus(graphId, status, nowIso, terminal = false) {
49709
50137
  this.db.prepare(`
@@ -49893,6 +50321,79 @@ ${blocks.join("\n\n")}`;
49893
50321
  const r = this.db.prepare(`SELECT * FROM mesh_graph_gates WHERE gate_id = ?`).get(gateId);
49894
50322
  return r ? mapGateRow(r) : null;
49895
50323
  }
50324
+ /** Reverse lookup from a graph node (kind `coordinator_gate`) to its gate row (phase C2). */
50325
+ findGateByNodeId(graphId, nodeId) {
50326
+ const r = this.db.prepare(
50327
+ `SELECT * FROM mesh_graph_gates WHERE graph_id = ? AND node_id = ?`
50328
+ ).get(graphId, nodeId);
50329
+ return r ? mapGateRow(r) : null;
50330
+ }
50331
+ listGatesByGraph(graphId) {
50332
+ const rows = this.db.prepare(
50333
+ `SELECT * FROM mesh_graph_gates WHERE graph_id = ? ORDER BY created_at ASC`
50334
+ ).all(graphId);
50335
+ return rows.map(mapGateRow);
50336
+ }
50337
+ /** Phase-C2 timeout sweep: gates of one mesh, optionally narrowed to states (e.g. awaiting/claimed). */
50338
+ listGatesByMesh(meshId, states) {
50339
+ if (states && states.length > 0) {
50340
+ const placeholders = states.map(() => "?").join(",");
50341
+ const rows2 = this.db.prepare(
50342
+ `SELECT * FROM mesh_graph_gates WHERE mesh_id = ? AND state IN (${placeholders}) ORDER BY created_at ASC`
50343
+ ).all(meshId, ...states);
50344
+ return rows2.map(mapGateRow);
50345
+ }
50346
+ const rows = this.db.prepare(
50347
+ `SELECT * FROM mesh_graph_gates WHERE mesh_id = ? ORDER BY created_at ASC`
50348
+ ).all(meshId);
50349
+ return rows.map(mapGateRow);
50350
+ }
50351
+ /**
50352
+ * Row write for phase C2 (claim / release / deadline sweep). `undefined`
50353
+ * fields are left unchanged; explicit `null` clears a nullable field. The
50354
+ * optional generation CAS rejects a stale fenced writer: the update lands
50355
+ * only while the row still carries `cas.leaseGeneration`.
50356
+ */
50357
+ patchGate(gateId, patch, nowIso, cas) {
50358
+ const current = this.getGate(gateId);
50359
+ if (!current) return false;
50360
+ if (cas && current.leaseGeneration !== cas.leaseGeneration) return false;
50361
+ const next = {
50362
+ state: patch.state ?? current.state,
50363
+ leaseOwnerSessionId: patch.leaseOwnerSessionId === void 0 ? current.leaseOwnerSessionId ?? null : patch.leaseOwnerSessionId,
50364
+ leaseGeneration: patch.leaseGeneration ?? current.leaseGeneration,
50365
+ fencingToken: patch.fencingToken === void 0 ? current.fencingToken ?? null : patch.fencingToken,
50366
+ leaseExpiresAt: patch.leaseExpiresAt === void 0 ? current.leaseExpiresAt ?? null : patch.leaseExpiresAt,
50367
+ deadlineAt: patch.deadlineAt === void 0 ? current.deadlineAt ?? null : patch.deadlineAt,
50368
+ releaseOutcome: patch.releaseOutcome === void 0 ? current.releaseOutcome ?? null : patch.releaseOutcome,
50369
+ releaseEvidenceJson: patch.releaseEvidenceJson === void 0 ? current.releaseEvidenceJson ?? null : patch.releaseEvidenceJson,
50370
+ releaseEvidenceDigest: patch.releaseEvidenceDigest === void 0 ? current.releaseEvidenceDigest ?? null : patch.releaseEvidenceDigest,
50371
+ releaseIdempotencyKey: patch.releaseIdempotencyKey === void 0 ? current.releaseIdempotencyKey ?? null : patch.releaseIdempotencyKey
50372
+ };
50373
+ const r = this.db.prepare(`
50374
+ UPDATE mesh_graph_gates
50375
+ SET state = ?, lease_owner_session_id = ?, lease_generation = ?, fencing_token = ?,
50376
+ lease_expires_at = ?, deadline_at = ?, release_outcome = ?,
50377
+ release_evidence_json = ?, release_evidence_digest = ?,
50378
+ release_idempotency_key = ?, updated_at = ?
50379
+ WHERE gate_id = ? AND lease_generation = ?
50380
+ `).run(
50381
+ next.state,
50382
+ next.leaseOwnerSessionId,
50383
+ next.leaseGeneration,
50384
+ next.fencingToken,
50385
+ next.leaseExpiresAt,
50386
+ next.deadlineAt,
50387
+ next.releaseOutcome,
50388
+ next.releaseEvidenceJson,
50389
+ next.releaseEvidenceDigest,
50390
+ next.releaseIdempotencyKey,
50391
+ nowIso,
50392
+ gateId,
50393
+ current.leaseGeneration
50394
+ );
50395
+ return r.changes > 0;
50396
+ }
49896
50397
  // ── mesh_graph_workspace_intents ─────────────────────────────────────────
49897
50398
  insertWorkspaceIntent(row) {
49898
50399
  this.db.prepare(`
@@ -51927,62 +52428,6 @@ ${blocks.join("\n\n")}`;
51927
52428
  MAX_CLONE_CHAIN = 16;
51928
52429
  }
51929
52430
  });
51930
- function loadDatabaseCtor() {
51931
- if (DatabaseCtor) return DatabaseCtor;
51932
- DatabaseCtor = loadBetterSqlite3();
51933
- return DatabaseCtor;
51934
- }
51935
- function safeMeshId(meshId) {
51936
- return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
51937
- }
51938
- function legacyQueuePath(meshId) {
51939
- return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
51940
- }
51941
- function cleanupStrayRootRuntimeDb(canonicalPath2) {
51942
- try {
51943
- const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
51944
- if (strayPath === canonicalPath2) return;
51945
- if (!(0, import_fs6.existsSync)(strayPath)) return;
51946
- if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
51947
- (0, import_fs6.unlinkSync)(strayPath);
51948
- if (!loggedStrayCleanup) {
51949
- loggedStrayCleanup = true;
51950
- LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
51951
- }
51952
- } catch (err) {
51953
- if (!loggedStrayCleanup) {
51954
- loggedStrayCleanup = true;
51955
- LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
51956
- }
51957
- }
51958
- }
51959
- function meshRuntimeStorePath() {
51960
- const dir = getLedgerDir2();
51961
- const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
51962
- cleanupStrayRootRuntimeDb(nextPath);
51963
- if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
51964
- const legacyPath = (0, import_path6.join)(dir, "beads.db");
51965
- if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
51966
- try {
51967
- (0, import_fs6.renameSync)(legacyPath, nextPath);
51968
- for (const suffix of ["-wal", "-shm"]) {
51969
- const legacyCompanion = `${legacyPath}${suffix}`;
51970
- if ((0, import_fs6.existsSync)(legacyCompanion)) {
51971
- (0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
51972
- }
51973
- }
51974
- } catch (err) {
51975
- if (!loggedMigrationFailure) {
51976
- loggedMigrationFailure = true;
51977
- LOG2.warn(
51978
- "MeshRuntimeStore",
51979
- `Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
51980
- );
51981
- }
51982
- return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
51983
- }
51984
- return nextPath;
51985
- }
51986
52431
  function meshTurnAttemptFromRow(r) {
51987
52432
  return {
51988
52433
  attemptId: r.attempt_id,
@@ -52040,15 +52485,82 @@ ${blocks.join("\n\n")}`;
52040
52485
  return { ledger: 0, toolCalls: 0, terminalQueue: 0, sessionDelivery: 0 };
52041
52486
  }
52042
52487
  }
52488
+ var MESH_EVENT_LEDGER_RETENTION_MS;
52489
+ var MESH_TOOL_CALL_LOG_RETENTION_MS;
52490
+ var MESH_TERMINAL_QUEUE_RETENTION_MS;
52491
+ var init_mesh_runtime_store_turn_rows = __esm2({
52492
+ "src/mesh/mesh-runtime-store-turn-rows.ts"() {
52493
+ "use strict";
52494
+ init_logger();
52495
+ init_mesh_retention_config();
52496
+ init_mesh_runtime_store();
52497
+ MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
52498
+ MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
52499
+ MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
52500
+ }
52501
+ });
52502
+ function loadDatabaseCtor() {
52503
+ if (DatabaseCtor) return DatabaseCtor;
52504
+ DatabaseCtor = loadBetterSqlite3();
52505
+ return DatabaseCtor;
52506
+ }
52507
+ function safeMeshId(meshId) {
52508
+ return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
52509
+ }
52510
+ function legacyQueuePath(meshId) {
52511
+ return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
52512
+ }
52513
+ function cleanupStrayRootRuntimeDb(canonicalPath2) {
52514
+ try {
52515
+ const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
52516
+ if (strayPath === canonicalPath2) return;
52517
+ if (!(0, import_fs6.existsSync)(strayPath)) return;
52518
+ if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
52519
+ (0, import_fs6.unlinkSync)(strayPath);
52520
+ if (!loggedStrayCleanup) {
52521
+ loggedStrayCleanup = true;
52522
+ LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
52523
+ }
52524
+ } catch (err) {
52525
+ if (!loggedStrayCleanup) {
52526
+ loggedStrayCleanup = true;
52527
+ LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
52528
+ }
52529
+ }
52530
+ }
52531
+ function meshRuntimeStorePath() {
52532
+ const dir = getLedgerDir2();
52533
+ const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
52534
+ cleanupStrayRootRuntimeDb(nextPath);
52535
+ if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
52536
+ const legacyPath = (0, import_path6.join)(dir, "beads.db");
52537
+ if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
52538
+ try {
52539
+ (0, import_fs6.renameSync)(legacyPath, nextPath);
52540
+ for (const suffix of ["-wal", "-shm"]) {
52541
+ const legacyCompanion = `${legacyPath}${suffix}`;
52542
+ if ((0, import_fs6.existsSync)(legacyCompanion)) {
52543
+ (0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
52544
+ }
52545
+ }
52546
+ } catch (err) {
52547
+ if (!loggedMigrationFailure) {
52548
+ loggedMigrationFailure = true;
52549
+ LOG2.warn(
52550
+ "MeshRuntimeStore",
52551
+ `Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
52552
+ );
52553
+ }
52554
+ return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
52555
+ }
52556
+ return nextPath;
52557
+ }
52043
52558
  var import_fs6;
52044
52559
  var import_path6;
52045
52560
  var DatabaseCtor;
52046
52561
  var loggedMigrationFailure;
52047
52562
  var loggedStrayCleanup;
52048
52563
  var MeshRuntimeStore;
52049
- var MESH_EVENT_LEDGER_RETENTION_MS;
52050
- var MESH_TOOL_CALL_LOG_RETENTION_MS;
52051
- var MESH_TERMINAL_QUEUE_RETENTION_MS;
52052
52564
  var init_mesh_runtime_store = __esm2({
52053
52565
  "src/mesh/mesh-runtime-store.ts"() {
52054
52566
  "use strict";
@@ -52058,13 +52570,14 @@ ${blocks.join("\n\n")}`;
52058
52570
  init_load_better_sqlite3();
52059
52571
  init_config();
52060
52572
  init_mesh_ledger();
52061
- init_mesh_retention_config();
52062
52573
  init_mesh_work_queue();
52063
52574
  init_mesh_graph_schema();
52064
52575
  init_mesh_graph_store();
52065
52576
  init_slot_model_enforcement();
52066
52577
  init_mesh_daemon_slot_axis();
52067
52578
  init_dist();
52579
+ init_mesh_runtime_store_turn_rows();
52580
+ init_mesh_runtime_store_turn_rows();
52068
52581
  loggedMigrationFailure = false;
52069
52582
  loggedStrayCleanup = false;
52070
52583
  MeshRuntimeStore = class _MeshRuntimeStore {
@@ -52260,6 +52773,7 @@ ${blocks.join("\n\n")}`;
52260
52773
  mesh_id TEXT NOT NULL,
52261
52774
  tool TEXT NOT NULL,
52262
52775
  session_id TEXT,
52776
+ caller_role TEXT,
52263
52777
  called_at INTEGER NOT NULL
52264
52778
  );
52265
52779
 
@@ -52589,6 +53103,10 @@ ${blocks.join("\n\n")}`;
52589
53103
  ON mesh_event_ledger(mesh_id, task_id, timestamp)
52590
53104
  WHERE task_id IS NOT NULL
52591
53105
  `);
53106
+ const toolCallCols = this.tableColumns("mesh_tool_call_log");
53107
+ if (!toolCallCols.has("caller_role")) {
53108
+ this.db.exec(`ALTER TABLE mesh_tool_call_log ADD COLUMN caller_role TEXT`);
53109
+ }
52592
53110
  } catch (err) {
52593
53111
  if (!loggedMigrationFailure) {
52594
53112
  loggedMigrationFailure = true;
@@ -53522,16 +54040,23 @@ ${blocks.join("\n\n")}`;
53522
54040
  * Returns a rate-limit advisory string when the call rate is too high, null otherwise.
53523
54041
  * windowMs: sliding window size in ms (default 10s)
53524
54042
  * maxCalls: max allowed calls within the window (default 5)
54043
+ *
54044
+ * `callerRole` is a diagnostic, not an auth boundary: it reflects whether
54045
+ * ADHDEV_COORDINATOR_SESSION_ID was present in this process's env at call
54046
+ * time, and a process can set that env var on itself. It exists to answer
54047
+ * "was this call made by a coordinator-launched process or not" for the
54048
+ * mesh-tool-call-caller-instrumentation investigation, not to gate access —
54049
+ * do not wire it into any block/allow decision.
53525
54050
  */
53526
54051
  recordMeshToolCall(opts) {
53527
- const { meshId, tool, sessionId = null } = opts;
54052
+ const { meshId, tool, sessionId = null, callerRole = null } = opts;
53528
54053
  const windowMs = opts.windowMs ?? 1e4;
53529
54054
  const maxCalls = opts.maxCalls ?? 5;
53530
54055
  const now = Date.now();
53531
54056
  const windowStart = now - windowMs;
53532
54057
  this.db.prepare(
53533
- "INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, called_at) VALUES (?, ?, ?, ?)"
53534
- ).run(meshId, tool, sessionId, now);
54058
+ "INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, caller_role, called_at) VALUES (?, ?, ?, ?, ?)"
54059
+ ).run(meshId, tool, sessionId, callerRole, now);
53535
54060
  const row = this.db.prepare(
53536
54061
  "SELECT COUNT(*) as cnt FROM mesh_tool_call_log WHERE mesh_id = ? AND tool = ? AND called_at >= ?"
53537
54062
  ).get(meshId, tool, windowStart);
@@ -53557,6 +54082,18 @@ ${blocks.join("\n\n")}`;
53557
54082
  pruneToolCallLog(olderThanMs) {
53558
54083
  return this.db.prepare("DELETE FROM mesh_tool_call_log WHERE called_at < ?").run(Date.now() - olderThanMs).changes;
53559
54084
  }
54085
+ /**
54086
+ * Read back recent mesh_tool_call_log rows for one mesh, most recent first.
54087
+ * Diagnostic reader for the MESH-TOOL-CALL-CALLER-INSTRUMENTATION 1단계
54088
+ * investigation (was session_id/caller_role actually getting recorded, and
54089
+ * does the coordinator/unknown split hold up) — not on any hot path.
54090
+ */
54091
+ getRecentToolCalls(meshId, limit = 100) {
54092
+ const rows = this.db.prepare(
54093
+ "SELECT tool, session_id, caller_role, called_at FROM mesh_tool_call_log WHERE mesh_id = ? ORDER BY called_at DESC LIMIT ?"
54094
+ ).all(meshId, limit);
54095
+ return rows.map((r) => ({ tool: r.tool, sessionId: r.session_id, callerRole: r.caller_role, calledAt: r.called_at }));
54096
+ }
53560
54097
  /**
53561
54098
  * Retention prune for mesh_event_ledger (SoT 1-11 (b)). The ledger is append-only
53562
54099
  * with NO lifecycle GC of its own, so lifecycle events accumulate without bound
@@ -54699,9 +55236,6 @@ ${blocks.join("\n\n")}`;
54699
55236
  return res.changes > 0;
54700
55237
  }
54701
55238
  };
54702
- MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
54703
- MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
54704
- MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
54705
55239
  }
54706
55240
  });
54707
55241
  var mesh_ledger_exports = {};
@@ -56255,7 +56789,7 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
56255
56789
  const lines = [
56256
56790
  "## Task difficulty",
56257
56791
  "",
56258
- "Pass `difficulty` on `mesh_enqueue_task` (`easy` / `medium` / `difficult` / `freeform`) to describe how hard the work is. It is a ROUTING HINT: it is matched against each node's capability slots, so a task goes to a slot configured for that difficulty.",
56792
+ "Pass `difficulty` on every worker entry in `mesh_enqueue_batch`, or on `mesh_enqueue_task` for the single-task fallback. The values (`easy` / `medium` / `difficult` / `freeform`) describe how hard the work is. It is a ROUTING HINT: it is matched against each node's capability slots, so a task goes to a slot configured for that difficulty.",
56259
56793
  "",
56260
56794
  '**The slot decides the model and thinking level \u2014 not the difficulty.** `difficulty: "difficult"` does not mean "use opus"; it means "route to a slot that handles difficult work", and that slot\'s own model/thinking is what launches. So classify honestly by how hard the task is, and change what a difficulty RUNS ON by editing the node\'s slots (`mesh_node_slots_set`), never by picking a different difficulty. Passing an explicit `model`/`thinkingLevel` still overrides everything for one task.'
56261
56795
  ];
@@ -56325,6 +56859,10 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
56325
56859
  if (policy.coordinatorIdlePushPolicy === "auto_silent_on_dispatch") {
56326
56860
  rules.push("- Delegated-worker completions are **auto-silenced**: the routine idle/completion push for a task you dispatch is suppressed once (approval-needed, failure, and long-running alerts still notify the owner normally)");
56327
56861
  }
56862
+ const failurePolicy = policy.onDependencyFailure === "cancel" ? "cancel" : "block";
56863
+ rules.push(
56864
+ `- on_dependency_failure: **${failurePolicy}** \u2014 controls downstream tasks when a required worker task fails or is cancelled. \`block\` (default) keeps downstream pending and automatically recovers if the predecessor is retried and later completes. \`cancel\` terminally cancels the dependent branch; it is not revived by predecessor retry.`
56865
+ );
56328
56866
  return `## Policy
56329
56867
  ${rules.join("\n")}`;
56330
56868
  }
@@ -56335,8 +56873,8 @@ ${rules.join("\n")}`;
56335
56873
  return `## Rules
56336
56874
 
56337
56875
  - **Route, don't implement.** Delegate all code reading, analysis, and execution to node agents. Never read source files or run commands in the coordinator \u2014 keep context lean. See also: **Never use local sub-agents** below.${destructiveGitRule}
56338
- - **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated to mesh nodes via \`mesh_enqueue_task\` / \`mesh_send_task\` (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
56339
- - **Front-load task messages.** Include everything the agent needs (files, problem, expected fix) in \`mesh_enqueue_task\` / \`mesh_send_task\`. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
56876
+ - **Never use local sub-agents.** Do NOT spawn your runtime's own sub-agents (e.g. Claude Code's Task/Explore/Agent tools, or any equivalent in-process agent-spawning tool) to read code, investigate, run RCA, or implement. Such sub-agents execute on the coordinator's machine, outside the mesh \u2014 they escape mesh parallelism, the ledger/audit trail, node capability profiles, and worktree isolation, and leave no \`mesh_task_history\` record. ALL code reading, analysis, RCA, and implementation must be delegated through \`mesh_enqueue_batch\` for a currently known multi-step graph, \`mesh_enqueue_task\` for one ready task, or \`mesh_send_task\` for a same-session continuation (use \`task_mode: "live_debug_readonly"\` for read-only investigation), or cross-verified via \`mesh_magi_review\` for read-only fan-out. The coordinator's own actions are limited to \`mesh_*\` tool orchestration and synthesizing results.
56877
+ - **Front-load immutable task instructions.** Include everything the agent needs (files, problem, expected fix) in \`mesh_enqueue_batch\` / \`mesh_enqueue_task\` / \`mesh_send_task\`. Put predecessor-produced data in explicit \`inputs_from\` bindings and coordinator decisions in gates; never copy untrusted worker output into a new instruction by hand when a binding can preserve provenance. Append a structured result request at the end: ask the worker to conclude with a JSON block containing \`status\`, \`changedFiles\`, \`gitStatus\`, \`validationResults\`, \`errors\`, \`nextAction\`. The daemon parses this automatically; you can read it from \`mesh_task_history\`.
56340
56878
  - **Reuse idle sessions.** For follow-up, retry, commit/push, or cleanup on the same issue, send only the delta to the existing idle session. Start a fresh session only when: (a) branch/worktree isolation is required, (b) the existing session had a dispatch failure or provider mismatch, (c) the transcript/runtime is contaminated or interrupted, (d) the user explicitly asks for a different provider/session, or (e) **the delta is a genuinely NEW subject rather than a continuation** \u2014 a new topic appended to an existing session can be dropped or re-run as the previous task, so give it its own task even when a session sits idle. Continuation of the same issue in an already-idle session is allowed and preferred \u2014 this rule blocks concurrent unrelated work interleaved into a live (still-generating) session, not sequential same-issue follow-ups. The test is subject continuity, not timing: carrying an investigation forward into its own fix is the SAME subject and belongs in that session (Workflow 3f), while an unrelated bug is a new subject even if the same session just went idle.
56341
56879
  - **Nodes are separate machines with separate checkouts \u2014 not interchangeable execution slots.** Each node is a different physical computer with its own clone of the repo. Work done on another node must be committed, pushed, and pulled back before this machine sees it, and since RELEASE/DEPLOY runs on the coordinator's own machine, sending a code change elsewhere buys a round trip out and another one back. So **default to this coordinator's own machine for code changes** \u2014 its local node (base or a worktree cloned from it). Routing to a DIFFERENT machine is the exception and needs a reason, of which there are exactly two: (a) **platform-specific verification** that cannot be done here \u2014 win32 PATH/registry, a clean install/uninstall on that OS, that machine's package-manager state; or (b) **parallelizing read-only investigation** across machines. "That node is idle" is not a reason. If you catch yourself dispatching a fix to another machine without (a) or (b), route it here instead.
56342
56880
  - **Don't split investigation from the fix.** When a task will plainly end in a code change, dispatch it as \`code_change\` from the start \u2014 the in-session handoff and split criteria live in Workflow 3f. Split only when the fix genuinely belongs on another machine for reason (a) above; redoing an investigator's context in a fresh session (worse, on another machine) is pure loss.
@@ -56400,9 +56938,12 @@ When you compose the task message you dispatch to a node, include this requireme
56400
56938
  |------|---------|
56401
56939
  | \`mesh_status\` | Nodes' health, git state, sessions, branch convergence |
56402
56940
  | \`mesh_list_nodes\` | List nodes with workspace paths |
56403
- | \`mesh_enqueue_task\` | Add a task to the pull-based queue; idle nodes auto-claim |
56404
- | \`mesh_enqueue_batch\` | Atomically enqueue a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK) |
56941
+ | \`mesh_enqueue_batch\` | **DEFAULT enqueue surface** for a plan with two or more known graph steps. Atomically enqueues a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK). Carries the full graph surface: \`inputs_from\`, \`run_if\`, \`gates\` + \`gated_by\`, \`workspaces\` + \`workspace_ref\` |
56942
+ | \`mesh_enqueue_task\` | SINGLE-TASK FALLBACK \u2014 one ready task with no declarable downstream step; idle nodes auto-claim |
56405
56943
  | \`mesh_view_queue\` | Queue status \u2014 pending/assigned/completed/failed/cancelled |
56944
+ | \`mesh_graph_view\` | Inspect orchestration graphs \u2014 node states, gates awaiting you, workspace sagas, why something is blocked |
56945
+ | \`mesh_graph_gate_claim\` | Take the lease on a gate awaiting a coordinator; returns the fencing token + generation a release needs |
56946
+ | \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate) |
56406
56947
  | \`mesh_queue_cancel\` | Cancel a queue task (audit history kept) |
56407
56948
  | \`mesh_queue_requeue\` | Return a task to pending for retry |
56408
56949
  | \`mesh_send_task\` | Push a task straight to a specific node/session |
@@ -56455,6 +56996,8 @@ When you compose the task message you dispatch to a node, include this requireme
56455
56996
  | \`mesh_coordinator_prompt_append_set\` | Write/clear that APPEND (append-only; base prompt is not replaceable) |`;
56456
56997
  TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
56457
56998
 
56999
+ Before searching for an enqueue tool, classify the whole currently known work frontier. For every new delegation search, include \`mesh_enqueue_batch\` by exact name; never search for or load only \`mesh_enqueue_task\`. Load \`mesh_enqueue_task\` only as the single-task fallback after the batch eligibility check below fails.
57000
+
56458
57001
  Before doing any coordinator work, confirm that the actual callable tool list includes \`mesh_status\` and the other \`mesh_*\` tools from the table above. If this Repo Mesh coordinator prompt is present but the callable \`mesh_*\` tools are missing, the MCP server/tool manifest is stale or not injected yet. Do not substitute terminal/file/git tools, do not inspect or edit the repository directly, and do not continue as a non-mesh local coding agent. Stop immediately and tell the user to run \`/reload-mcp\` or start a fresh coordinator session so ADHDev can reconnect \`adhdev-mesh\`.`;
56459
57002
  QUOTA_SECTION = `## Provider Quota
56460
57003
 
@@ -56465,9 +57008,15 @@ Before doing any coordinator work, confirm that the actual callable tool list in
56465
57008
  WORKFLOW_SECTION = `## Orchestration Workflow
56466
57009
 
56467
57010
  1. **Assess** \u2014 Call \`mesh_status\` to see which nodes are healthy and available. Check \`mesh_task_history\` to understand what has already been done in this mesh \u2014 previous delegations, completions, and failures.
56468
- 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then attach every enqueued task with \`mission_id\`. Express "B after A" ordering with \`depends_on\` on the queue task instead of waiting and polling \u2014 the system claims dependents automatically when their dependencies complete. When the plan is a multi-task graph, submit it with ONE \`mesh_enqueue_batch\` call instead of N sequential enqueues: the batch inserts atomically (a mid-batch error such as a cycle or bad difficulty rolls the whole set back, so no half-registered chain), and each task's \`depends_on\` may name sibling tasks by their batch-local \`ref\` label, forward references included. When the mission's outcome is decided, update its status (\`completed\`/\`abandoned\`) via \`mesh_mission_upsert\`. If the prompt already shows an **Active Mission**, continue it from its current task state \u2014 do not re-enqueue tasks that already exist.
57011
+ 2. **Plan** \u2014 Decompose the user's request into independent tasks for parallel execution, or sequential tasks when dependencies exist. If \`mesh_task_history\` shows a recent failure for a task, decide whether to retry or reassign. **For multi-task work, create a mission first**: call \`mesh_mission_upsert\` with a title and goal, then attach every enqueued task with \`mission_id\`. Express "B after A" ordering with \`depends_on\` instead of waiting and polling \u2014 the system claims dependents automatically when their dependencies complete. When the mission's outcome is decided, update its status (\`completed\`/\`abandoned\`) via \`mesh_mission_upsert\`. If the prompt already shows an **Active Mission**, continue it from its current task state \u2014 do not re-enqueue tasks that already exist.
56469
57012
  3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
56470
- a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
57013
+ a. **Batch-first rule:** submit one \`mesh_enqueue_batch\` whenever the currently known plan contains two or more graph steps. A graph may include worker tasks, selected predecessor outputs via \`inputs_from\`, declarative \`run_if\` branches, delayed \`workspace_ref\` preparation, and coordinator gates for approval, Refinery, CI waiting, publish, or deployment. Submit the whole materializable plan once; do not wait and enqueue each known step separately.
57014
+
57015
+ Use \`mesh_enqueue_task\` only when exactly one new worker task is currently known and there is no known downstream step to declare, or when continuing an already-running session via \`mesh_send_task\` is the correct same-subject handoff. If later work is genuinely unknown, enqueue the one ready task and start a new batch when the next frontier becomes known.
57016
+
57017
+ Do not invent speculative downstream instructions merely to form a batch. Use an \`inputs_from\` binding when the instruction is stable but needs predecessor evidence; use a coordinator gate when a human/coordinator decision or external side effect must happen first; use \`run_if\` for a structured result predicate. If none can state the future step faithfully, single-task enqueue is correct.
57018
+
57019
+ Either way, idle node agents automatically pull tasks from the queue and begin working. A batch inserts atomically (a mid-batch error such as a cycle or bad difficulty rolls the whole set back, so no half-registered chain), and each task's \`depends_on\` may name sibling tasks by their batch-local \`ref\` label, forward references included.
56471
57020
  b. **Node Preparation**: Reuse an existing idle session on the correct node/provider before launching a new chat/session. Call \`mesh_launch_session\` only when no suitable session exists, when the user explicitly asks for a fresh provider/session, or when branch/worktree isolation requires it. **A node is not limited to one live session for read-only work** \u2014 \`readonly\`/\`live_debug_readonly\` tasks are exempt from the one-active-per-node invariant, so the SAME node can auto-launch multiple concurrent read-only sessions with no worktree needed. Cloning a worktree costs roughly 10 seconds, so it is cheap enough to create one whenever write work needs a free node; use it for branch isolation, for parallel write tasks (one active write per node), or when a node's read-only queue is deep enough that a second node would clearly finish faster \u2014 call \`mesh_clone_node\` to create the worktree node first.
56472
57021
  b0. **Base nodes are for environment-specific testing, not for general code changes.** Before dispatching any write task, answer ONE question: *does this task verify the physical environment of a specific machine or OS, or does it only change code?*
56473
57022
  - **Physical-environment task \u2192 base node, targeted.** Pin it with \`required_tags\` (e.g. \`["os=win32"]\`) or \`target_node_id\`. Examples that genuinely require the real machine: verifying a win32 \`PATH\`/registry/installer layout, a clean-install or uninstall on a specific OS, Homebrew or package-manager state on one particular machine, an OS-dependent runtime behavior (path separators, process spawn, native bindings), or reproducing a bug reported only on that node. A worktree CANNOT substitute for these \u2014 the point is the machine itself.
@@ -59764,6 +60313,593 @@ ${rendered}`, "utf-8");
59764
60313
  ACTIVE_SAGA_STATES = ["declared", "preparing", "compensating"];
59765
60314
  }
59766
60315
  });
60316
+ function readGateSpec(node) {
60317
+ if (!node) return {};
60318
+ try {
60319
+ const parsed = JSON.parse(node.baseSpecJson);
60320
+ return parsed && typeof parsed === "object" ? parsed : {};
60321
+ } catch {
60322
+ return {};
60323
+ }
60324
+ }
60325
+ function isoAfter(nowMs, seconds) {
60326
+ return new Date(nowMs + seconds * 1e3).toISOString();
60327
+ }
60328
+ function claimMeshGraphGate(input) {
60329
+ const store = MeshRuntimeStore.getInstance();
60330
+ const nowMs = input.nowMs ?? Date.now();
60331
+ const nowIso = new Date(nowMs).toISOString();
60332
+ const outboxEvents = [];
60333
+ const result = store.transaction(() => {
60334
+ const graphStore = store.graphStore();
60335
+ const gate = graphStore.getGate(input.gateId);
60336
+ if (!gate || gate.meshId !== input.meshId) return { claimed: false, reason: "gate_not_found" };
60337
+ if (gate.eligibleCoordinatorSessionId && gate.eligibleCoordinatorSessionId !== input.coordinatorSessionId) {
60338
+ return { claimed: false, reason: "gate_not_eligible" };
60339
+ }
60340
+ if (gate.state === "released" || gate.state === "cancelled") {
60341
+ return { claimed: false, reason: `gate_terminal:${gate.state}` };
60342
+ }
60343
+ if (gate.state === "declared") {
60344
+ return { claimed: false, reason: "gate_not_awaiting" };
60345
+ }
60346
+ const leaseLive = !!gate.leaseExpiresAt && gate.leaseExpiresAt > nowIso;
60347
+ const sameOwner = gate.leaseOwnerSessionId === input.coordinatorSessionId;
60348
+ const priorState = gate.state;
60349
+ let takeoverFromClaimed = false;
60350
+ if (priorState === "claimed" && leaseLive && !sameOwner) {
60351
+ return { claimed: false, reason: "gate_lease_held", gate };
60352
+ }
60353
+ if (priorState === "expired" && gate.onTimeout !== "hold") {
60354
+ return { claimed: false, reason: "gate_terminal:expired", gate };
60355
+ }
60356
+ const node = graphStore.getNode(gate.graphId, gate.nodeId);
60357
+ const spec = readGateSpec(node);
60358
+ const leaseSeconds = input.leaseSeconds ?? spec.lease_seconds ?? MESH_GATE_DEFAULT_LEASE_SECONDS;
60359
+ const leaseExpiresAt = isoAfter(nowMs, leaseSeconds);
60360
+ let generation;
60361
+ let fencingToken;
60362
+ if (priorState === "claimed" && sameOwner && leaseLive) {
60363
+ generation = gate.leaseGeneration;
60364
+ fencingToken = gate.fencingToken;
60365
+ } else {
60366
+ takeoverFromClaimed = priorState === "claimed";
60367
+ generation = gate.leaseGeneration + 1;
60368
+ fencingToken = (0, import_crypto13.randomUUID)();
60369
+ }
60370
+ const deadlineAt = input.extendDeadlineSeconds && input.extendDeadlineSeconds > 0 ? isoAfter(nowMs, input.extendDeadlineSeconds) : gate.deadlineAt;
60371
+ const won = graphStore.patchGate(gate.gateId, {
60372
+ state: "claimed",
60373
+ leaseOwnerSessionId: input.coordinatorSessionId,
60374
+ leaseGeneration: generation,
60375
+ fencingToken,
60376
+ leaseExpiresAt,
60377
+ ...deadlineAt !== void 0 ? { deadlineAt } : {}
60378
+ }, nowIso, { leaseGeneration: gate.leaseGeneration });
60379
+ if (!won) return { claimed: false, reason: "gate_claim_race" };
60380
+ if (node && node.state === "expired") {
60381
+ graphStore.updateNodeState(gate.graphId, gate.nodeId, "awaiting_coordinator", nowIso);
60382
+ }
60383
+ if (takeoverFromClaimed) {
60384
+ outboxEvents.push({
60385
+ kind: "graph_gate_lease_expired",
60386
+ payload: {
60387
+ graphId: gate.graphId,
60388
+ gateId: gate.gateId,
60389
+ ref: gate.ref,
60390
+ previousOwnerSessionId: gate.leaseOwnerSessionId,
60391
+ previousGeneration: gate.leaseGeneration,
60392
+ newGeneration: generation
60393
+ }
60394
+ });
60395
+ }
60396
+ outboxEvents.push({
60397
+ kind: "graph_gate_claimed",
60398
+ payload: {
60399
+ graphId: gate.graphId,
60400
+ gateId: gate.gateId,
60401
+ ref: gate.ref,
60402
+ action: gate.action,
60403
+ ownerSessionId: input.coordinatorSessionId,
60404
+ generation,
60405
+ leaseExpiresAt
60406
+ }
60407
+ });
60408
+ for (const event of outboxEvents) {
60409
+ graphStore.insertOutboxEvent({
60410
+ id: newMeshGraphOutboxId(),
60411
+ meshId: gate.meshId,
60412
+ graphId: gate.graphId,
60413
+ kind: event.kind,
60414
+ payload: JSON.stringify(event.payload),
60415
+ status: "pending",
60416
+ attemptCount: 0,
60417
+ createdAt: nowIso,
60418
+ updatedAt: nowIso
60419
+ });
60420
+ }
60421
+ return {
60422
+ claimed: true,
60423
+ gate: graphStore.getGate(gate.gateId),
60424
+ leaseGeneration: generation,
60425
+ fencingToken,
60426
+ leaseExpiresAt,
60427
+ deadlineAt,
60428
+ ambiguousExternalOutcome: takeoverFromClaimed || priorState === "expired" && gate.leaseGeneration > 0,
60429
+ previousLeaseOwnerSessionId: gate.leaseOwnerSessionId
60430
+ };
60431
+ });
60432
+ if (result.claimed) {
60433
+ try {
60434
+ drainMeshGraphOutbox(input.meshId);
60435
+ } catch {
60436
+ }
60437
+ }
60438
+ return result;
60439
+ }
60440
+ function releaseMeshGraphGate(input) {
60441
+ const store = MeshRuntimeStore.getInstance();
60442
+ const nowMs = input.nowMs ?? Date.now();
60443
+ const nowIso = new Date(nowMs).toISOString();
60444
+ const result = store.transaction(() => {
60445
+ const graphStore = store.graphStore();
60446
+ const gate = graphStore.getGate(input.gateId);
60447
+ if (!gate || gate.meshId !== input.meshId) throw new Error(`gate_not_found: no gate '${input.gateId}'`);
60448
+ const releaseDigest = sha256Hex2(canonicalJson({
60449
+ outcome: input.outcome,
60450
+ result: input.result ?? null,
60451
+ evidence: input.evidence ?? null,
60452
+ patches: input.patches ?? []
60453
+ }));
60454
+ if (gate.releaseIdempotencyKey === input.idempotencyKey) {
60455
+ if (gate.releaseEvidenceDigest === releaseDigest) {
60456
+ return { released: true, duplicate: true, gate, materializedNodeIds: [] };
60457
+ }
60458
+ throw new Error(
60459
+ `gate_release_conflict: idempotency key '${input.idempotencyKey}' already committed with a different release digest (gate '${input.gateId}')`
60460
+ );
60461
+ }
60462
+ if (gate.state === "released") {
60463
+ throw new Error(`gate_already_released: gate '${input.gateId}' was released under a different idempotency key`);
60464
+ }
60465
+ if (gate.state !== "claimed") {
60466
+ throw new Error(`gate_not_claimed: gate '${input.gateId}' is '${gate.state}' \u2014 only a claimed gate can be released`);
60467
+ }
60468
+ if (input.fencingToken !== gate.fencingToken || input.leaseGeneration !== gate.leaseGeneration) {
60469
+ throw new Error(
60470
+ `stale_fence: gate '${input.gateId}' is at lease generation ${gate.leaseGeneration}; the presented token/generation (${input.leaseGeneration}) is stale`
60471
+ );
60472
+ }
60473
+ if (gate.leaseExpiresAt && gate.leaseExpiresAt <= nowIso) {
60474
+ throw new Error(
60475
+ `gate_lease_expired: gate '${input.gateId}' lease expired at ${gate.leaseExpiresAt} \u2014 reclaim the gate (higher generation) and reconcile external evidence before releasing`
60476
+ );
60477
+ }
60478
+ const node = graphStore.getNode(gate.graphId, gate.nodeId);
60479
+ if (!node) throw new Error(`graph_node_not_found: gate '${input.gateId}' backs a missing node '${gate.nodeId}'`);
60480
+ const nodes = graphStore.listNodes(gate.graphId);
60481
+ const edges = graphStore.listEdges(gate.graphId);
60482
+ const byId = new Map(nodes.map((n) => [n.nodeId, n]));
60483
+ const incoming = edges.filter((e) => e.toNodeId === node.nodeId);
60484
+ const upstreamSettled = incoming.every((e) => {
60485
+ const sourceState = byId.get(e.fromNodeId)?.state;
60486
+ return sourceState === "completed" || sourceState === "released" || sourceState === "skipped" && e.omitOnSkip;
60487
+ });
60488
+ if (!upstreamSettled) {
60489
+ throw new Error(`gate_upstream_unsettled: gate '${input.gateId}' has unsatisfied predecessors`);
60490
+ }
60491
+ const directDownstream = new Set(edges.filter((e) => e.fromNodeId === node.nodeId).map((e) => e.toNodeId));
60492
+ for (const patch of input.patches ?? []) {
60493
+ const target = nodes.find((n) => n.nodeId === patch.node || n.ref === patch.node);
60494
+ if (!target || !directDownstream.has(target.nodeId)) {
60495
+ throw new Error(`gate_patch_not_downstream: '${patch.node}' is not a direct downstream node of gate '${input.gateId}'`);
60496
+ }
60497
+ for (const key2 of Object.keys(patch.baseSpecPatch)) {
60498
+ if (!MESH_GATE_RELEASE_PATCH_KEYS.includes(key2)) {
60499
+ throw new Error(`gate_patch_forbidden: key '${key2}' is outside the permitted release patch surface (design :294-299)`);
60500
+ }
60501
+ }
60502
+ if (target.queueTaskId) {
60503
+ const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
60504
+ if (entry && entry.status !== "pending") {
60505
+ throw new Error(
60506
+ `task_already_claimed: gate release cannot patch node '${target.nodeId}' \u2014 queue task '${target.queueTaskId}' is '${entry.status}' and immutable (design :334)`
60507
+ );
60508
+ }
60509
+ }
60510
+ const rawSpec = (() => {
60511
+ try {
60512
+ return JSON.parse(target.baseSpecJson);
60513
+ } catch {
60514
+ return {};
60515
+ }
60516
+ })();
60517
+ graphStore.updateNodeBaseSpec(
60518
+ target.graphId,
60519
+ target.nodeId,
60520
+ JSON.stringify({ ...rawSpec && typeof rawSpec === "object" ? rawSpec : {}, ...patch.baseSpecPatch }),
60521
+ nowIso
60522
+ );
60523
+ const inMap = byId.get(target.nodeId);
60524
+ if (inMap) inMap.materializationVersion += 1;
60525
+ }
60526
+ const won = graphStore.patchGate(gate.gateId, {
60527
+ state: "released",
60528
+ releaseOutcome: input.outcome,
60529
+ releaseEvidenceJson: canonicalJson({ result: input.result ?? null, evidence: input.evidence ?? null }),
60530
+ releaseEvidenceDigest: releaseDigest,
60531
+ releaseIdempotencyKey: input.idempotencyKey
60532
+ }, nowIso, { leaseGeneration: gate.leaseGeneration });
60533
+ if (!won) throw new Error(`stale_fence: gate '${input.gateId}' moved under the release (generation race)`);
60534
+ graphStore.updateNodeState(gate.graphId, node.nodeId, "released", nowIso);
60535
+ const mapped = byId.get(node.nodeId);
60536
+ if (mapped) mapped.state = "released";
60537
+ const materializedNodeIds = advanceReleasedGateDownstream(store, gate, node, nowIso);
60538
+ const freshNodes = graphStore.listNodes(gate.graphId);
60539
+ const graph = graphStore.getGraph(gate.graphId);
60540
+ if (graph && freshNodes.every(isTerminalEquivalent)) {
60541
+ graphStore.updateGraphStatus(gate.graphId, "completed", nowIso, true);
60542
+ insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_completed", { graphId: gate.graphId }, nowIso);
60543
+ } else if (graph?.status === "waiting_gate") {
60544
+ const stillWaiting = graphStore.listGatesByGraph(gate.graphId).some((g) => g.gateId !== gate.gateId && (g.state === "awaiting_coordinator" || g.state === "claimed"));
60545
+ if (!stillWaiting) graphStore.updateGraphStatus(gate.graphId, "active", nowIso);
60546
+ }
60547
+ insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_gate_released", {
60548
+ graphId: gate.graphId,
60549
+ gateId: gate.gateId,
60550
+ nodeId: node.nodeId,
60551
+ ref: gate.ref,
60552
+ action: gate.action,
60553
+ outcome: input.outcome,
60554
+ releaseDigest,
60555
+ generation: gate.leaseGeneration,
60556
+ materializedNodeIds
60557
+ }, nowIso);
60558
+ if (materializedNodeIds.length > 0) {
60559
+ insertGateOutbox(graphStore, gate.meshId, gate.graphId, "queue_wake", {
60560
+ meshId: gate.meshId,
60561
+ reason: "graph_gate_released",
60562
+ graphId: gate.graphId,
60563
+ gateId: gate.gateId
60564
+ }, nowIso);
60565
+ }
60566
+ return { released: true, duplicate: false, gate: graphStore.getGate(gate.gateId), materializedNodeIds };
60567
+ });
60568
+ try {
60569
+ drainMeshGraphOutbox(input.meshId);
60570
+ } catch {
60571
+ }
60572
+ return result;
60573
+ }
60574
+ function advanceReleasedGateDownstream(store, gate, gateNode, nowIso) {
60575
+ const graphStore = store.graphStore();
60576
+ const nodes = graphStore.listNodes(gate.graphId);
60577
+ const edges = graphStore.listEdges(gate.graphId);
60578
+ const byId = new Map(nodes.map((n) => [n.nodeId, n]));
60579
+ const mappedGate = byId.get(gateNode.nodeId);
60580
+ if (mappedGate) mappedGate.state = "released";
60581
+ const materialized = [];
60582
+ let frontier = /* @__PURE__ */ new Set([gateNode.nodeId]);
60583
+ for (let pass = 0; pass < nodes.length + 1 && frontier.size > 0; pass += 1) {
60584
+ const nextFrontier = /* @__PURE__ */ new Set();
60585
+ const downstreamIds = new Set(edges.filter((e) => frontier.has(e.fromNodeId)).map((e) => e.toNodeId));
60586
+ for (const targetId of downstreamIds) {
60587
+ const target = byId.get(targetId);
60588
+ if (!target) continue;
60589
+ if (target.kind === "coordinator_gate") {
60590
+ maybeOpenCoordinatorGate(store, target, edges, byId, nowIso);
60591
+ continue;
60592
+ }
60593
+ if (target.kind !== "worker_task" || !target.queueTaskId) continue;
60594
+ if (target.state !== "declared" && target.state !== "blocked") continue;
60595
+ clearReleasedGateHold(store, target, gate.gateId);
60596
+ const outcome = settleDownstreamNode(store, target, edges, byId, nowIso);
60597
+ if (outcome.kind === "materialized") {
60598
+ materialized.push(target.nodeId);
60599
+ insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_materialized", {
60600
+ graphId: target.graphId,
60601
+ nodeId: target.nodeId,
60602
+ ref: target.ref,
60603
+ taskId: target.queueTaskId,
60604
+ gateId: gate.gateId,
60605
+ materializationVersion: target.materializationVersion,
60606
+ digest: outcome.digest,
60607
+ receipts: outcome.receipts
60608
+ }, nowIso);
60609
+ } else if (outcome.kind === "skipped") {
60610
+ nextFrontier.add(target.nodeId);
60611
+ insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_skipped", {
60612
+ graphId: target.graphId,
60613
+ nodeId: target.nodeId,
60614
+ ref: target.ref,
60615
+ taskId: target.queueTaskId,
60616
+ gateId: gate.gateId,
60617
+ reason: outcome.reason
60618
+ }, nowIso);
60619
+ } else if (outcome.kind === "error") {
60620
+ insertGateOutbox(graphStore, target.meshId, target.graphId, "graph_node_materialization_failed", {
60621
+ graphId: target.graphId,
60622
+ nodeId: target.nodeId,
60623
+ ref: target.ref,
60624
+ taskId: target.queueTaskId,
60625
+ gateId: gate.gateId,
60626
+ blockedReason: outcome.blockedReason
60627
+ }, nowIso);
60628
+ } else {
60629
+ const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
60630
+ if (entry && entry.status === "pending" && !entry.blockedReason) {
60631
+ entry.blockedReason = graphMaterializationBlockReason(target.nodeId, target.materializationVersion);
60632
+ store.updateQueueEntry(entry);
60633
+ }
60634
+ }
60635
+ }
60636
+ frontier = nextFrontier;
60637
+ }
60638
+ return materialized;
60639
+ }
60640
+ function clearReleasedGateHold(store, target, gateId) {
60641
+ if (!target.queueTaskId) return;
60642
+ const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
60643
+ if (!entry || entry.status !== "pending") return;
60644
+ const hold = parseCoordinatorGateBlock(entry.blockedReason);
60645
+ if (hold && hold.gateId === gateId) {
60646
+ delete entry.blockedReason;
60647
+ store.updateQueueEntry(entry);
60648
+ }
60649
+ }
60650
+ function sweepMeshGraphGateTimeouts(meshId, nowMs) {
60651
+ const store = MeshRuntimeStore.getInstance();
60652
+ const now = nowMs ?? Date.now();
60653
+ const nowIso = new Date(now).toISOString();
60654
+ const result = store.transaction(() => {
60655
+ const graphStore = store.graphStore();
60656
+ const candidates = graphStore.listGatesByMesh(meshId, ["awaiting_coordinator", "claimed"]);
60657
+ const expiredLeaseGateIds = candidates.filter((g) => g.state === "claimed" && g.leaseExpiresAt && g.leaseExpiresAt <= nowIso).map((g) => g.gateId);
60658
+ const expiredGateIds = [];
60659
+ for (const gate of candidates) {
60660
+ if (!gate.deadlineAt || gate.deadlineAt > nowIso) continue;
60661
+ const node = graphStore.getNode(gate.graphId, gate.nodeId);
60662
+ const won = graphStore.patchGate(gate.gateId, { state: "expired" }, nowIso, { leaseGeneration: gate.leaseGeneration });
60663
+ if (!won) continue;
60664
+ if (node && node.state !== "expired") {
60665
+ graphStore.updateNodeState(gate.graphId, gate.nodeId, "expired", nowIso);
60666
+ }
60667
+ let cancelledNodeIds = [];
60668
+ if (gate.onTimeout === "cancel_downstream" && node) {
60669
+ cancelledNodeIds = cancelGateDownstreamSubtree(store, node, nowIso);
60670
+ } else if (gate.onTimeout === "fail_graph") {
60671
+ graphStore.updateGraphStatus(gate.graphId, "failed", nowIso, true);
60672
+ }
60673
+ insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_gate_expired", {
60674
+ graphId: gate.graphId,
60675
+ gateId: gate.gateId,
60676
+ nodeId: gate.nodeId,
60677
+ ref: gate.ref,
60678
+ action: gate.action,
60679
+ policy: gate.onTimeout,
60680
+ deadlineAt: gate.deadlineAt,
60681
+ urgent: true,
60682
+ ...cancelledNodeIds.length > 0 ? { cancelledNodeIds } : {}
60683
+ }, nowIso);
60684
+ expiredGateIds.push(gate.gateId);
60685
+ }
60686
+ return { expiredGateIds, expiredLeaseGateIds };
60687
+ });
60688
+ if (result.expiredGateIds.length > 0) {
60689
+ try {
60690
+ drainMeshGraphOutbox(meshId);
60691
+ } catch {
60692
+ }
60693
+ }
60694
+ if (result.expiredGateIds.length > 0 || result.expiredLeaseGateIds.length > 0) {
60695
+ LOG2.info("MeshGraph", `Gate sweep for mesh ${meshId}: expired=[${result.expiredGateIds.join(",")}] lease-lapsed=[${result.expiredLeaseGateIds.join(",")}]`);
60696
+ }
60697
+ return result;
60698
+ }
60699
+ function isCancelExempt(state) {
60700
+ return state === "completed" || state === "released" || state === "skipped" || state === "cancelled" || state === "failed" || state === "expired";
60701
+ }
60702
+ function cancelGateDownstreamSubtree(store, gateNode, nowIso) {
60703
+ const graphStore = store.graphStore();
60704
+ const nodes = graphStore.listNodes(gateNode.graphId);
60705
+ const edges = graphStore.listEdges(gateNode.graphId);
60706
+ const byId = new Map(nodes.map((n) => [n.nodeId, n]));
60707
+ const cancelled = [];
60708
+ const visited = /* @__PURE__ */ new Set([gateNode.nodeId]);
60709
+ let frontier = [gateNode.nodeId];
60710
+ while (frontier.length > 0) {
60711
+ const next = [];
60712
+ for (const id of frontier) {
60713
+ for (const edge of edges.filter((e) => e.fromNodeId === id)) {
60714
+ if (visited.has(edge.toNodeId)) continue;
60715
+ visited.add(edge.toNodeId);
60716
+ const target = byId.get(edge.toNodeId);
60717
+ if (!target || isCancelExempt(target.state)) continue;
60718
+ graphStore.updateNodeState(target.graphId, target.nodeId, "cancelled", nowIso, {
60719
+ failureReason: `coordinator_gate_timeout:${gateNode.nodeId}`
60720
+ });
60721
+ if (target.queueTaskId) {
60722
+ const entry = store.findQueueEntryById(target.meshId, target.queueTaskId);
60723
+ if (entry && entry.status === "pending") {
60724
+ entry.status = "cancelled";
60725
+ entry.blockedReason = `coordinator_gate_timeout:${gateNode.nodeId}`;
60726
+ store.updateQueueEntry(entry);
60727
+ }
60728
+ }
60729
+ cancelled.push(target.nodeId);
60730
+ next.push(target.nodeId);
60731
+ }
60732
+ }
60733
+ frontier = next;
60734
+ }
60735
+ return cancelled;
60736
+ }
60737
+ function insertGateOutbox(graphStore, meshId, graphId, kind, payload, nowIso) {
60738
+ graphStore.insertOutboxEvent({
60739
+ id: newMeshGraphOutboxId(),
60740
+ meshId,
60741
+ graphId,
60742
+ kind,
60743
+ payload: JSON.stringify(payload),
60744
+ status: "pending",
60745
+ attemptCount: 0,
60746
+ createdAt: nowIso,
60747
+ updatedAt: nowIso
60748
+ });
60749
+ }
60750
+ var import_crypto13;
60751
+ var MESH_GATE_DEFAULT_LEASE_SECONDS;
60752
+ var MESH_GATE_NAMED_OUTCOMES;
60753
+ var MESH_GATE_RELEASE_PATCH_KEYS;
60754
+ var init_mesh_graph_gates = __esm2({
60755
+ "src/mesh/mesh-graph-gates.ts"() {
60756
+ "use strict";
60757
+ import_crypto13 = require("crypto");
60758
+ init_mesh_runtime_store();
60759
+ init_logger();
60760
+ init_mesh_graph_input_binding();
60761
+ init_mesh_graph_types();
60762
+ init_mesh_graph_transition_runner();
60763
+ init_mesh_graph_transition_runner();
60764
+ MESH_GATE_DEFAULT_LEASE_SECONDS = 900;
60765
+ MESH_GATE_NAMED_OUTCOMES = ["passed", "failed", "rejected"];
60766
+ MESH_GATE_RELEASE_PATCH_KEYS = ["run_if", "on_false", "inputs_from", "workspace_ref"];
60767
+ }
60768
+ });
60769
+ function normalizeOrchestrationDecision(raw, surface) {
60770
+ const source = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
60771
+ const readCount = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : void 0;
60772
+ const readyWorkerTasks = readCount(source.ready_worker_tasks ?? source.readyWorkerTasks);
60773
+ const knownGraphSteps = readCount(source.known_graph_steps ?? source.knownGraphSteps);
60774
+ const rawReason = source.single_reason ?? source.singleReason;
60775
+ const singleReason = typeof rawReason === "string" && rawReason.trim() ? rawReason.trim() : null;
60776
+ const rawBlockers = source.capability_blockers ?? source.capabilityBlockers;
60777
+ const blockers = Array.isArray(rawBlockers) ? rawBlockers.filter((b) => typeof b === "string" && b.trim().length > 0).map((b) => b.trim()) : [];
60778
+ const decision = {
60779
+ decision: surface,
60780
+ ...readyWorkerTasks !== void 0 ? { ready_worker_tasks: readyWorkerTasks } : {},
60781
+ ...knownGraphSteps !== void 0 ? { known_graph_steps: knownGraphSteps } : {},
60782
+ single_reason: surface === "single" ? singleReason : null,
60783
+ ...blockers.length > 0 ? { capability_blockers: blockers } : {}
60784
+ };
60785
+ const result = { decision };
60786
+ if (surface !== "single") return result;
60787
+ const superseded = [singleReason, ...blockers].find(
60788
+ (value) => !!value && MESH_SUPERSEDED_SINGLE_REASONS.includes(value)
60789
+ );
60790
+ if (superseded) {
60791
+ result.batchCapabilityAvailable = {
60792
+ code: "batch_capability_available",
60793
+ reportedReason: superseded,
60794
+ message: `'${superseded}' is no longer a blocker: mesh_enqueue_batch supports selected predecessor outputs (inputs_from), delayed worktree preparation (workspace_ref) and coordinator gates. Declare the whole known plan in one batch instead of enqueueing the steps separately.`
60795
+ };
60796
+ }
60797
+ if (knownGraphSteps !== void 0 && knownGraphSteps >= 2) {
60798
+ result.declaredEligibleSingle = true;
60799
+ }
60800
+ return result;
60801
+ }
60802
+ function safeAppend(meshId, kind, payload, taskId) {
60803
+ try {
60804
+ appendLedgerEntry(meshId, { kind, ...taskId ? { taskId } : {}, payload });
60805
+ } catch (e) {
60806
+ LOG2.warn("MeshGraph", `Ledger provenance append (${kind}) failed: ${e?.message || e}`);
60807
+ }
60808
+ }
60809
+ function recordGraphEnqueueCommitted(meshId, p) {
60810
+ safeAppend(meshId, "graph_enqueue_committed", {
60811
+ graphId: p.graphId,
60812
+ batchId: p.batchId,
60813
+ enqueueSurface: p.enqueueSurface,
60814
+ schemaVersion: p.schemaVersion,
60815
+ planDigest: p.planDigest,
60816
+ ...p.missionId ? { missionId: p.missionId } : {},
60817
+ ...p.coordinatorSessionId ? { coordinatorSessionId: p.coordinatorSessionId } : {},
60818
+ taskCount: p.taskCount,
60819
+ gateCount: p.gateCount,
60820
+ workspaceCount: p.workspaceCount,
60821
+ dependencyEdgeCount: p.dependencyEdgeCount,
60822
+ onDependencyFailure: p.onDependencyFailure,
60823
+ ...p.orchestrationDecision ? { orchestrationDecision: p.orchestrationDecision } : {},
60824
+ ...p.replayed ? { replayed: true } : {}
60825
+ });
60826
+ }
60827
+ function recordGraphEnqueueValidationFailed(meshId, p) {
60828
+ safeAppend(meshId, "graph_enqueue_validation_failed", {
60829
+ code: p.code,
60830
+ ...p.batchId ? { batchId: p.batchId } : {},
60831
+ ...p.taskCount !== void 0 ? { taskCount: p.taskCount } : {},
60832
+ ...p.gateCount !== void 0 ? { gateCount: p.gateCount } : {},
60833
+ ...p.workspaceCount !== void 0 ? { workspaceCount: p.workspaceCount } : {},
60834
+ ...p.orchestrationDecision ? { orchestrationDecision: p.orchestrationDecision } : {}
60835
+ });
60836
+ }
60837
+ function recordGraphEnqueueRolledBack(meshId, p) {
60838
+ safeAppend(meshId, "graph_enqueue_rolled_back", {
60839
+ code: p.code,
60840
+ ...p.batchId ? { batchId: p.batchId } : {},
60841
+ ...p.taskCount !== void 0 ? { taskCount: p.taskCount } : {},
60842
+ // The error STRING is a validation message authored by this codebase
60843
+ // (never worker output or a task message), so it is safe provenance.
60844
+ ...p.error ? { error: p.error.slice(0, 500) } : {}
60845
+ });
60846
+ }
60847
+ function recordGraphGateClaimed(meshId, p) {
60848
+ safeAppend(meshId, "graph_gate_claimed", {
60849
+ graphId: p.graphId,
60850
+ gateId: p.gateId,
60851
+ ...p.ref ? { ref: p.ref } : {},
60852
+ action: p.action,
60853
+ generation: p.generation,
60854
+ ownerSessionId: p.ownerSessionId,
60855
+ ...p.leaseExpiresAt ? { leaseExpiresAt: p.leaseExpiresAt } : {},
60856
+ ...p.ambiguousExternalOutcome ? { ambiguousExternalOutcome: true } : {},
60857
+ ...p.previousLeaseOwnerSessionId ? { previousLeaseOwnerSessionId: p.previousLeaseOwnerSessionId } : {}
60858
+ });
60859
+ }
60860
+ function recordGraphGateReleased(meshId, p) {
60861
+ safeAppend(meshId, "graph_gate_released", {
60862
+ graphId: p.graphId,
60863
+ gateId: p.gateId,
60864
+ ...p.ref ? { ref: p.ref } : {},
60865
+ action: p.action,
60866
+ outcome: p.outcome,
60867
+ generation: p.generation,
60868
+ ...p.releaseDigest ? { releaseDigest: p.releaseDigest } : {},
60869
+ ...p.materializedNodeIds?.length ? { materializedNodeIds: p.materializedNodeIds } : {},
60870
+ ...p.duplicate ? { duplicate: true } : {}
60871
+ });
60872
+ }
60873
+ function recordGraphGateExpired(meshId, p) {
60874
+ safeAppend(meshId, "graph_gate_expired", {
60875
+ gateId: p.gateId,
60876
+ ...p.graphId ? { graphId: p.graphId } : {},
60877
+ policy: p.policy,
60878
+ ...p.deadlineAt ? { deadlineAt: p.deadlineAt } : {},
60879
+ ...p.cancelledNodeIds?.length ? { cancelledNodeIds: p.cancelledNodeIds } : {}
60880
+ });
60881
+ }
60882
+ var MESH_VALID_SINGLE_REASONS;
60883
+ var MESH_SUPERSEDED_SINGLE_REASONS;
60884
+ var init_mesh_graph_provenance = __esm2({
60885
+ "src/mesh/mesh-graph-provenance.ts"() {
60886
+ "use strict";
60887
+ init_mesh_ledger();
60888
+ init_logger();
60889
+ MESH_VALID_SINGLE_REASONS = [
60890
+ "only_one_step_known",
60891
+ "future_step_not_specifiable",
60892
+ "same_session_continuation",
60893
+ "legacy_client",
60894
+ "operator_override"
60895
+ ];
60896
+ MESH_SUPERSEDED_SINGLE_REASONS = [
60897
+ "output_needed",
60898
+ "workspace_unresolved",
60899
+ "coordinator_action_between"
60900
+ ];
60901
+ }
60902
+ });
59767
60903
  function turnProjectionActiveWorkStatus(meshId, taskId) {
59768
60904
  const row = resolveTurnAttemptRow({ meshId, taskId });
59769
60905
  if (!row) return null;
@@ -61805,15 +62941,15 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61805
62941
  }
61806
62942
  return "";
61807
62943
  }
61808
- function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
61809
- const turnEnd = selectFinalAssistantTurnEndMessage(messages);
62944
+ function extractFinalAssistantSummaryEvidence(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS, opts) {
62945
+ const turnEnd = selectFinalAssistantTurnEndMessage(messages, opts);
61810
62946
  if (!turnEnd) return { finalSummary: "" };
61811
62947
  return {
61812
62948
  finalSummary: flattenContent(turnEnd.content).trim().slice(0, maxChars),
61813
62949
  transcriptMessageAt: readChatMessageTimestampIso(turnEnd)
61814
62950
  };
61815
62951
  }
61816
- function selectFinalAssistantTurnEndMessage(messages) {
62952
+ function selectFinalAssistantTurnEndMessage(messages, opts) {
61817
62953
  if (!Array.isArray(messages) || messages.length === 0) return null;
61818
62954
  let chromeFallbacks = 0;
61819
62955
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -61828,6 +62964,13 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61828
62964
  chromeFallbacks++;
61829
62965
  continue;
61830
62966
  }
62967
+ const boundaryMs = opts?.turnStartedAtMs;
62968
+ if (typeof boundaryMs === "number" && Number.isFinite(boundaryMs)) {
62969
+ const msgAt = readChatMessageTimestampMs(msg);
62970
+ if (typeof msgAt === "number" && Number.isFinite(msgAt) && msgAt >= boundaryMs && msgAt - boundaryMs < FINAL_ANSWER_MIN_TURN_AGE_MS && Date.now() - msgAt < FINAL_ANSWER_MIN_TURN_AGE_MS) {
62971
+ return null;
62972
+ }
62973
+ }
61831
62974
  return msg;
61832
62975
  }
61833
62976
  return null;
@@ -62164,6 +63307,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
62164
63307
  return (Array.isArray(messages) ? messages : []).filter((message) => classifyChatMessageVisibility(message).surface === surface);
62165
63308
  }
62166
63309
  var DEFAULT_FINAL_SUMMARY_MAX_CHARS;
63310
+ var FINAL_ANSWER_MIN_TURN_AGE_MS;
62167
63311
  var FINAL_SUMMARY_CHROME_FALLBACK_MAX_DEPTH;
62168
63312
  var TRANSCRIPT_CHROME_HARD_LINE_PATTERNS;
62169
63313
  var TRANSCRIPT_CHROME_PANEL_LINE_PATTERNS;
@@ -62187,6 +63331,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
62187
63331
  "use strict";
62188
63332
  init_contracts2();
62189
63333
  DEFAULT_FINAL_SUMMARY_MAX_CHARS = 16e3;
63334
+ FINAL_ANSWER_MIN_TURN_AGE_MS = 3e4;
62190
63335
  FINAL_SUMMARY_CHROME_FALLBACK_MAX_DEPTH = 2;
62191
63336
  TRANSCRIPT_CHROME_HARD_LINE_PATTERNS = [
62192
63337
  // Bottom status bar: "auto K3 thinking: high ~/Work/adhdev main [±]"
@@ -66765,7 +67910,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66765
67910
  const fingerprint = `${target}::${buildPendingEventFingerprint(fingerprintSource)}`;
66766
67911
  try {
66767
67912
  const inserted = store.insertPendingEvent({
66768
- id: (0, import_crypto13.randomUUID)(),
67913
+ id: (0, import_crypto15.randomUUID)(),
66769
67914
  meshId: UNRESOLVED_FORWARD_OUTBOX_MESH_ID,
66770
67915
  coordinatorDaemonId: target,
66771
67916
  event,
@@ -66828,14 +67973,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66828
67973
  return 0;
66829
67974
  }
66830
67975
  }
66831
- var import_crypto13;
67976
+ var import_crypto15;
66832
67977
  var UNRESOLVED_FORWARD_OUTBOX_MESH_ID;
66833
67978
  var UNRESOLVED_FORWARD_MAX_AGE_MS;
66834
67979
  var retryNudgeHandler;
66835
67980
  var init_mesh_unresolved_forward_outbox = __esm2({
66836
67981
  "src/mesh/mesh-unresolved-forward-outbox.ts"() {
66837
67982
  "use strict";
66838
- import_crypto13 = require("crypto");
67983
+ import_crypto15 = require("crypto");
66839
67984
  init_logger();
66840
67985
  init_mesh_runtime_store();
66841
67986
  init_mesh_events_pending();
@@ -69514,6 +70659,15 @@ ${cleanBody}`;
69514
70659
  }
69515
70660
  };
69516
70661
  }
70662
+ if (admission.kind === "admit") {
70663
+ traceMeshEventStage(
70664
+ "terminal_admission_admitted",
70665
+ traceCtx,
70666
+ `${admission.evidenceLevel}:${admission.reason}`
70667
+ );
70668
+ } else {
70669
+ traceMeshEventStage("terminal_admission_not_enforced", traceCtx, admission.reason);
70670
+ }
69517
70671
  }
69518
70672
  if (!readNonEmptyString(args.metadataEvent.taskId) && !sessionHasActiveAssignment(args.meshId, eventSessionId) && !findRecentTerminalLedgerEvidence({ meshId: args.meshId, sessionId: eventSessionId, nodeId: eventNodeId || void 0 }) && isWeakCompletionEvidence(args.metadataEvent)) {
69519
70673
  LOG2.info("MeshEvents", `Suppressed agent:generating_completed for session ${eventSessionId} (mesh ${args.meshId}): no taskId, no active assignment, and no prior terminal ledger evidence \u2014 a pre-dispatch startup/greeting artifact, not a real task completion`);
@@ -69876,13 +71030,13 @@ ${cleanBody}`;
69876
71030
  const emittedAfterFinalizationTimeout = completionDiagnostic?.emittedAfterFinalizationTimeout === true;
69877
71031
  const weakEvidenceAtEntry = isWeakCompletionEvidence(args.metadataEvent);
69878
71032
  const weakCompleted = outcome === "completed" && weakEvidenceAtEntry && !emittedAfterFinalizationTimeout;
69879
- if (outcome === "completed" && emittedAfterFinalizationTimeout && weakEvidenceAtEntry) {
71033
+ if (emittedAfterFinalizationTimeout && weakEvidenceAtEntry) {
69880
71034
  args.metadataEvent.reviewRecommended = true;
69881
71035
  args.metadataEvent.completionDiagnostic = {
69882
71036
  ...completionDiagnostic || {},
69883
71037
  timedOutWithoutFinalAssistant: true
69884
71038
  };
69885
- LOG2.info("MeshQueue", `Completion for session ${sessionId} exhausted the finalization wait with no confirmed final assistant \u2014 flipping terminal (never wedge) but flagged reviewRecommended`);
71039
+ LOG2.info("MeshQueue", `Completion for session ${sessionId} exhausted the finalization wait with no confirmed final assistant \u2014 recording FORCED TERMINATION (outcome=${outcome}; terminal, never wedge) flagged reviewRecommended; NOT a completion`);
69886
71040
  }
69887
71041
  let reducerAllowsFlip = true;
69888
71042
  if (eventTaskId && !weakCompleted) {
@@ -70006,6 +71160,7 @@ ${cleanBody}`;
70006
71160
  return task ? { id: task.id, taskMode: task.taskMode } : null;
70007
71161
  }
70008
71162
  let completedTaskForLedger = null;
71163
+ let forcedTimeoutNoResponse = false;
70009
71164
  let directDispatchTaskIdForLedger;
70010
71165
  let worktreeHasQueuedTask = false;
70011
71166
  if (args.event === "agent:generating_completed") {
@@ -70015,7 +71170,8 @@ ${cleanBody}`;
70015
71170
  if (sessionId) {
70016
71171
  directDispatchTaskIdForLedger = readNonEmptyString(args.metadataEvent.taskId) || resolveActiveDirectDispatchTaskId(args.meshId, sessionId);
70017
71172
  const isFalseIdle = isFalseIdleCompletion(args.metadataEvent);
70018
- completedTaskForLedger = markSessionTerminal(sessionId, "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
71173
+ forcedTimeoutNoResponse = isWeakCompletionEvidence(args.metadataEvent) && (args.metadataEvent.completionDiagnostic && typeof args.metadataEvent.completionDiagnostic === "object" && args.metadataEvent.completionDiagnostic.emittedAfterFinalizationTimeout === true) === true;
71174
+ completedTaskForLedger = markSessionTerminal(sessionId, forcedTimeoutNoResponse ? "failed" : "completed", eventTimestamp, { tentativeIfDirect: isFalseIdle });
70019
71175
  if (nodeId && providerType) {
70020
71176
  if (!isFalseIdle) {
70021
71177
  sweepExpiredRemoteIdleSessions();
@@ -70038,7 +71194,7 @@ ${cleanBody}`;
70038
71194
  }
70039
71195
  }
70040
71196
  const completedTaskId = completedTaskForLedger?.id;
70041
- if (completedTaskId && hasPendingDependents(args.meshId, completedTaskId)) {
71197
+ if (completedTaskId && !forcedTimeoutNoResponse && hasPendingDependents(args.meshId, completedTaskId)) {
70042
71198
  setImmediate(() => {
70043
71199
  triggerMeshQueue(components, args.meshId).catch((e) => {
70044
71200
  LOG2.warn("MeshQueue", `Dependent wake after task ${completedTaskId} failed: ${e?.message || e}`);
@@ -70264,7 +71420,7 @@ ${cleanBody}`;
70264
71420
  });
70265
71421
  });
70266
71422
  }
70267
- const ledgerKind = EVENT_TO_LEDGER_KIND[args.event];
71423
+ const ledgerKind = forcedTimeoutNoResponse ? "task_failed" : EVENT_TO_LEDGER_KIND[args.event];
70268
71424
  if (ledgerKind) {
70269
71425
  try {
70270
71426
  const ledgerNodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId) || void 0;
@@ -72466,6 +73622,20 @@ ${cleanBody}`;
72466
73622
  }
72467
73623
  return newest;
72468
73624
  }
73625
+ function escapeTailFingerprint(messages) {
73626
+ let fp = "";
73627
+ for (const msg of messages) {
73628
+ if (!msg) continue;
73629
+ const ts2 = readChatMessageTimestampMs(msg) ?? 0;
73630
+ let len = -1;
73631
+ try {
73632
+ len = JSON.stringify(msg.content)?.length ?? -1;
73633
+ } catch {
73634
+ }
73635
+ fp += `${msg.role}:${ts2}:${len};`;
73636
+ }
73637
+ return fp;
73638
+ }
72469
73639
  async function reconcileUnterminatedDirectDispatches(components, mesh, selfIds, localDaemonId) {
72470
73640
  const dispatches = getActiveDirectDispatches(mesh.id);
72471
73641
  rehydrateAckedHoldsForMesh(mesh.id);
@@ -72476,6 +73646,9 @@ ${cleanBody}`;
72476
73646
  for (const key2 of heldKeys) {
72477
73647
  if (!activeTaskKeys.has(key2)) deleteHoldState(key2, mesh.id);
72478
73648
  }
73649
+ for (const key2 of [...nonIdleEscapeTracks.keys()]) {
73650
+ if (key2.startsWith(`${mesh.id}::`) && !activeTaskKeys.has(key2)) nonIdleEscapeTracks.delete(key2);
73651
+ }
72479
73652
  if (dispatches.length === 0) return;
72480
73653
  const dispatchMeshCommand = components.dispatchMeshCommand;
72481
73654
  const nodeById = new Map(mesh.nodes.map((n) => [n.id, n]));
@@ -72540,23 +73713,43 @@ ${cleanBody}`;
72540
73713
  consecutiveReadFailures: 0,
72541
73714
  ...priorHoldState?.transcriptIdleSinceMs !== void 0 ? { transcriptIdleSinceMs: priorHoldState.transcriptIdleSinceMs } : {}
72542
73715
  });
72543
- let nonIdleEscapeSinceAckMs = null;
72544
73716
  const nowMs = Date.now();
72545
73717
  if (readChatPayloadStatus(payload) !== "idle") {
72546
73718
  if (isAcked && readChatPayloadStatus(payload) === "generating") {
72547
- const escapeAckedAtMs = Date.parse(readNonEmptyString(dispatch.updatedAt));
72548
- const escapeSinceAckMs = Number.isFinite(escapeAckedAtMs) ? nowMs - escapeAckedAtMs : Number.POSITIVE_INFINITY;
72549
- if (escapeSinceAckMs >= resolveAckedDeathDeadlineMs()) {
72550
- nonIdleEscapeSinceAckMs = escapeSinceAckMs;
73719
+ const escapeMessages = Array.isArray(payload.messages) ? payload.messages : [];
73720
+ const fingerprint = escapeTailFingerprint(escapeMessages);
73721
+ const prior = nonIdleEscapeTracks.get(synthKey);
73722
+ if (!prior || prior.tailFingerprint !== fingerprint) {
73723
+ nonIdleEscapeTracks.set(synthKey, { anchorMs: nowMs, tailFingerprint: fingerprint, candidateRecorded: false });
73724
+ if (prior) {
73725
+ LOG2.info("MeshReconcile", `Non-idle escape re-armed: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) reads 'generating' and its transcript tail moved \u2014 worker is alive mid-turn; the escape deadline restarts from now`);
73726
+ }
73727
+ } else {
73728
+ const staticMs = nowMs - prior.anchorMs;
73729
+ if (staticMs >= resolveAckedDeathDeadlineMs() && !prior.candidateRecorded) {
73730
+ prior.candidateRecorded = true;
73731
+ const candidateEvidence = extractFinalAssistantSummaryEvidence(escapeMessages);
73732
+ LOG2.warn("MeshReconcile", `Non-idle escape WEAK CANDIDATE (no completion): task ${taskId} on node ${nodeId} (mesh ${mesh.id}) has read 'generating' with a static transcript tail for ${Math.round(staticMs / 1e3)}s (deadline ${Math.round(resolveAckedDeathDeadlineMs() / 1e3)}s)${candidateEvidence.finalSummary ? " and a final-looking assistant bubble" : ""} \u2014 recorded as a weak candidate only; NOT completed (a timeout is never completion evidence); the hold/reclaim nets own the terminal`);
73733
+ traceMeshEventDrop("non_idle_escape_weak_candidate_held", {
73734
+ taskId,
73735
+ sessionId,
73736
+ nodeId,
73737
+ meshId: mesh.id,
73738
+ event: "agent:generating_completed"
73739
+ }, `staticTailMs=${staticMs} finalAssistant=${candidateEvidence.finalSummary ? "present" : "absent"} transcriptAt=${candidateEvidence.transcriptMessageAt ?? "n/a"}`);
73740
+ }
72551
73741
  }
72552
73742
  }
72553
- if (nonIdleEscapeSinceAckMs === null) {
72554
- setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
72555
- continue;
72556
- }
73743
+ setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
73744
+ continue;
72557
73745
  }
72558
73746
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
72559
- const evidence = extractFinalAssistantSummaryEvidence(messages);
73747
+ const dispatchedAtMs = Date.parse(readNonEmptyString(dispatch.dispatchedAt));
73748
+ const evidence = extractFinalAssistantSummaryEvidence(
73749
+ messages,
73750
+ void 0,
73751
+ Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
73752
+ );
72560
73753
  if (hasTrailingToolActivityAfterFinalAssistant(messages)) {
72561
73754
  setHoldState(synthKey, mesh.id, { liveConfirmedSinceAck: true, consecutiveReadFailures: 0 });
72562
73755
  LOG2.info("MeshReconcile", `Mid-turn causal admission: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 the latest final-looking assistant bubble is followed by trailing tool/terminal activity (interim narration; the turn is still executing); holding the transcript synth`);
@@ -72588,7 +73781,7 @@ ${cleanBody}`;
72588
73781
  const deathDeadlineMs = resolveAckedDeathDeadlineMs();
72589
73782
  const holdState = getHoldState(synthKey, mesh.id);
72590
73783
  let fastTrackReady = false;
72591
- if (nonIdleEscapeSinceAckMs === null && evidence.finalSummary) {
73784
+ if (evidence.finalSummary) {
72592
73785
  const idleSinceMs = holdState?.transcriptIdleSinceMs ?? nowMs;
72593
73786
  if (holdState && holdState.transcriptIdleSinceMs === void 0) {
72594
73787
  setHoldState(synthKey, mesh.id, { ...holdState, transcriptIdleSinceMs: idleSinceMs });
@@ -72609,9 +73802,7 @@ ${cleanBody}`;
72609
73802
  }
72610
73803
  if (!fastTrackReady) {
72611
73804
  backstopKind = "ackedHoldDeathDeadlineFired";
72612
- if (nonIdleEscapeSinceAckMs === null) {
72613
- LOG2.warn("MeshReconcile", `Acked-hold death deadline reached: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) still idle ${Math.round(sinceAckMs / 1e3)}s after the ack (deadline ${Math.round(deathDeadlineMs / 1e3)}s) \u2014 synthesizing the missing completion as a notification-loss net (a real emit, if it ever lands, no-ops idempotently).`);
72614
- }
73805
+ LOG2.warn("MeshReconcile", `Acked-hold death deadline reached: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) still idle ${Math.round(sinceAckMs / 1e3)}s after the ack (deadline ${Math.round(deathDeadlineMs / 1e3)}s) \u2014 synthesizing the missing completion as a notification-loss net (a real emit, if it ever lands, no-ops idempotently).`);
72615
73806
  }
72616
73807
  }
72617
73808
  if (realTerminalEmitPendingForTask(mesh.id, taskId)) {
@@ -72620,21 +73811,8 @@ ${cleanBody}`;
72620
73811
  continue;
72621
73812
  }
72622
73813
  if (!evidence.finalSummary) continue;
72623
- const dispatchedAtMs = Date.parse(readNonEmptyString(dispatch.dispatchedAt));
72624
73814
  const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
72625
- if (nonIdleEscapeSinceAckMs !== null) {
72626
- if (!Number.isFinite(dispatchedAtMs) || !Number.isFinite(transcriptAtMs) || transcriptAtMs < dispatchedAtMs) {
72627
- LOG2.info("MeshReconcile", `Non-idle-escape stale-summary guard: refusing transcript synth for task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 final assistant message (${evidence.transcriptMessageAt ?? "unparseable"}) is not provably at/after this task's dispatch (${dispatch.dispatchedAt})`);
72628
- traceMeshEventDrop("reconcile_non_idle_escape_stale_summary", {
72629
- taskId,
72630
- sessionId,
72631
- nodeId,
72632
- meshId: mesh.id,
72633
- event: "agent:generating_completed"
72634
- }, `transcriptAt=${evidence.transcriptMessageAt} dispatchedAt=${dispatch.dispatchedAt}`);
72635
- continue;
72636
- }
72637
- } else if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
73815
+ if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
72638
73816
  LOG2.info("MeshReconcile", `Stale-summary guard: skipping transcript reconcile for task ${taskId} on node ${nodeId} (mesh ${mesh.id}) \u2014 final assistant message (${evidence.transcriptMessageAt}) predates this task's dispatch (${dispatch.dispatchedAt}); it is a prior task's summary`);
72639
73817
  traceMeshEventDrop("reconcile_stale_summary_before_dispatch", {
72640
73818
  taskId,
@@ -72645,15 +73823,12 @@ ${cleanBody}`;
72645
73823
  }, `transcriptAt=${evidence.transcriptMessageAt} < dispatchedAt=${dispatch.dispatchedAt}`);
72646
73824
  continue;
72647
73825
  }
72648
- const reprobeStatus = nonIdleEscapeSinceAckMs !== null ? null : await reprobeWorkerStatus(components, { isLocalNode, nodeDaemonId, readArgs });
73826
+ const reprobeStatus = await reprobeWorkerStatus(components, { isLocalNode, nodeDaemonId, readArgs });
72649
73827
  if (reprobeStatus && reprobeStatus !== "idle") {
72650
73828
  deleteHoldState(synthKey, mesh.id);
72651
73829
  LOG2.info("MeshReconcile", `Live re-probe defer: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) read '${reprobeStatus}' at synth-commit time \u2014 worker resumed generating; deferring synth to a later tick`);
72652
73830
  continue;
72653
73831
  }
72654
- if (nonIdleEscapeSinceAckMs !== null) {
72655
- LOG2.warn("MeshReconcile", `Acked-hold death deadline NON-IDLE escape: task ${taskId} on node ${nodeId} (mesh ${mesh.id}) still reads 'generating' ${Math.round(nonIdleEscapeSinceAckMs / 1e3)}s after the ack with a causally-proven final assistant (transcriptAt=${evidence.transcriptMessageAt ?? "n/a"}) \u2014 the floor-class PTY wedge rescue net; synthesizing the missing completion (a real emit, if it ever lands, no-ops idempotently).`);
72656
- }
72657
73832
  const providerSessionId = readNonEmptyString(payload.providerSessionId);
72658
73833
  const workerSession = components.instanceManager.getInstance(sessionId);
72659
73834
  const workerCoordinatorDaemonId = readNonEmptyString(
@@ -72814,8 +73989,12 @@ ${cleanBody}`;
72814
73989
  if (!payload) return declined("chat_tail_unreadable", "worker transcript read returned no payload (offline/unreachable?)");
72815
73990
  const payloadStatus = readChatPayloadProviderObservedStatus(payload);
72816
73991
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
72817
- const evidence = extractFinalAssistantSummaryEvidence(messages);
72818
73992
  const dispatchedAtMs = Date.parse(readNonEmptyString(row.dispatchTimestamp));
73993
+ const evidence = extractFinalAssistantSummaryEvidence(
73994
+ messages,
73995
+ void 0,
73996
+ Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
73997
+ );
72819
73998
  const transcriptAtMs = Date.parse(evidence.transcriptMessageAt ?? "");
72820
73999
  if (Number.isFinite(dispatchedAtMs) && Number.isFinite(transcriptAtMs) && transcriptAtMs < dispatchedAtMs) {
72821
74000
  return declined(
@@ -72861,7 +74040,10 @@ ${cleanBody}`;
72861
74040
  `dispatchTimestamp=${row.dispatchTimestamp ?? "none"} transcriptMessageAt=${evidence.transcriptMessageAt ?? "none"} \u2014 shape-only evidence (${verdict.reason}) cannot complete an undated tail; reclaim owns it`
72862
74041
  );
72863
74042
  }
72864
- const selectedFinalAssistant = finalAssistantPresent ? selectFinalAssistantTurnEndMessage(messages) : null;
74043
+ const selectedFinalAssistant = finalAssistantPresent ? selectFinalAssistantTurnEndMessage(
74044
+ messages,
74045
+ Number.isFinite(dispatchedAtMs) ? { turnStartedAtMs: dispatchedAtMs } : void 0
74046
+ ) : null;
72865
74047
  const selectedFinalAssistantIndex = selectedFinalAssistant ? messages.lastIndexOf(selectedFinalAssistant) : -1;
72866
74048
  const admissionSnapshot = {
72867
74049
  producer,
@@ -72897,6 +74079,7 @@ ${cleanBody}`;
72897
74079
  admissionSnapshot
72898
74080
  };
72899
74081
  }
74082
+ var nonIdleEscapeTracks;
72900
74083
  var init_mesh_completion_synthesis = __esm2({
72901
74084
  "src/mesh/mesh-completion-synthesis.ts"() {
72902
74085
  "use strict";
@@ -72917,6 +74100,7 @@ ${cleanBody}`;
72917
74100
  init_mesh_reconcile_v2_backstop();
72918
74101
  init_mesh_reconcile_acked_hold();
72919
74102
  init_mesh_remote_event_pull();
74103
+ nonIdleEscapeTracks = /* @__PURE__ */ new Map();
72920
74104
  }
72921
74105
  });
72922
74106
  function findLiveCoordinators(components) {
@@ -73527,6 +74711,53 @@ ${cleanBody}`;
73527
74711
  producer: "early_idle_transcript_evidence"
73528
74712
  });
73529
74713
  if (terminalEvidence) {
74714
+ const weakCandidateKey = `${meshId}::${row.id}`;
74715
+ if (terminalEvidence.evidenceLevel === "weak") {
74716
+ const candidate = weakCompletionCandidateStreak.get(weakCandidateKey) ?? { count: 0 };
74717
+ if (candidate.finalAssistantAt !== void 0 && candidate.finalAssistantAt !== terminalEvidence.transcriptMessageAt) {
74718
+ candidate.count = 0;
74719
+ }
74720
+ candidate.finalAssistantAt = terminalEvidence.transcriptMessageAt;
74721
+ candidate.count += 1;
74722
+ weakCompletionCandidateStreak.set(weakCandidateKey, candidate);
74723
+ if (candidate.count === 1) {
74724
+ const nodeLabel = row.assignedNodeId ? `Node '${row.assignedNodeId}'` : "Remote agent";
74725
+ const metadataEvent = {
74726
+ evidenceLevel: "weak",
74727
+ finalSummary: terminalEvidence.finalSummary,
74728
+ taskId: row.id,
74729
+ providerType: readNonEmptyString(row.assignedProviderType) || terminalEvidence.providerType,
74730
+ providerSessionId: terminalEvidence.providerSessionId,
74731
+ source: "early_idle_transcript_evidence",
74732
+ targetSessionId: terminalEvidence.sessionId
74733
+ };
74734
+ try {
74735
+ queuePendingMeshCoordinatorEvent({
74736
+ event: "agent:generating_completed",
74737
+ meshId,
74738
+ nodeLabel,
74739
+ nodeId: row.assignedNodeId,
74740
+ metadataEvent,
74741
+ coordinatorMessage: buildMeshSystemMessage({ event: "agent:generating_completed", nodeLabel, metadataEvent }),
74742
+ queuedAt: nowMs
74743
+ });
74744
+ } catch {
74745
+ }
74746
+ }
74747
+ if (candidate.count < WEAK_COMPLETION_CANDIDATE_CONFIRM_TICKS) {
74748
+ traceMeshEventDrop("weak_completion_candidate_held", {
74749
+ taskId: row.id,
74750
+ sessionId: row.assignedSessionId,
74751
+ nodeId: row.assignedNodeId,
74752
+ meshId,
74753
+ event: "agent:generating_completed"
74754
+ }, `weak message-shape completion candidate ${candidate.count}/${WEAK_COMPLETION_CANDIDATE_CONFIRM_TICKS} (early-idle) \u2014 awaiting re-confirmation`);
74755
+ continue;
74756
+ }
74757
+ weakCompletionCandidateStreak.delete(weakCandidateKey);
74758
+ } else {
74759
+ weakCompletionCandidateStreak.delete(weakCandidateKey);
74760
+ }
73530
74761
  const propagation = propagateWatchdogTranscriptCompletion(
73531
74762
  components,
73532
74763
  meshId,
@@ -74466,6 +75697,30 @@ ${cleanBody}`;
74466
75697
  LOG2.warn("MeshReconcile", `Completion reconcile failed for mesh ${mesh.id}: ${e?.message || e}`);
74467
75698
  }
74468
75699
  }
75700
+ if (store) {
75701
+ for (const mesh of listMeshes()) {
75702
+ const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
75703
+ if (!daemonHostsMesh(mesh, selfIds)) continue;
75704
+ try {
75705
+ const gateStore = store.graphStore();
75706
+ const preSweep = new Map(
75707
+ gateStore.listGatesByMesh(mesh.id, ["awaiting_coordinator", "claimed"]).map((g) => [g.gateId, { graphId: g.graphId, onTimeout: g.onTimeout, deadlineAt: g.deadlineAt }])
75708
+ );
75709
+ const swept = sweepMeshGraphGateTimeouts(mesh.id);
75710
+ for (const gateId of swept.expiredGateIds) {
75711
+ const meta3 = preSweep.get(gateId);
75712
+ recordGraphGateExpired(mesh.id, {
75713
+ gateId,
75714
+ graphId: meta3?.graphId,
75715
+ policy: meta3?.onTimeout ?? "hold",
75716
+ deadlineAt: meta3?.deadlineAt
75717
+ });
75718
+ }
75719
+ } catch (e) {
75720
+ LOG2.warn("MeshReconcile", `Graph gate sweep failed for mesh ${mesh.id}: ${e?.message || e}`);
75721
+ }
75722
+ }
75723
+ }
74469
75724
  if (store) {
74470
75725
  for (const mesh of listMeshes()) {
74471
75726
  const selfIds = resolveCoordinatorSelfIds(mesh, drainDaemonIds);
@@ -74768,6 +76023,8 @@ ${cleanBody}`;
74768
76023
  init_mesh_reconcile_stranded_dispatch();
74769
76024
  init_mesh_reconcile_unresolved_forward();
74770
76025
  init_mesh_graph_workspace_saga();
76026
+ init_mesh_graph_gates();
76027
+ init_mesh_graph_provenance();
74771
76028
  init_mesh_reconcile_v2_backstop();
74772
76029
  init_mesh_reconcile_acked_hold();
74773
76030
  init_mesh_reconcile_coordinator_drain();
@@ -77195,6 +78452,29 @@ ${cleanBody}`;
77195
78452
  "use strict";
77196
78453
  }
77197
78454
  });
78455
+ function resolveRefineVitestMaxWorkers(env2 = process.env) {
78456
+ const raw = env2[REFINE_VITEST_MAX_WORKERS_ENV];
78457
+ if (raw === void 0 || raw.trim() === "") return DEFAULT_REFINE_VITEST_MAX_WORKERS;
78458
+ const parsed = Number.parseInt(raw, 10);
78459
+ if (!Number.isFinite(parsed)) return DEFAULT_REFINE_VITEST_MAX_WORKERS;
78460
+ if (parsed === 0) return null;
78461
+ return Math.max(1, parsed);
78462
+ }
78463
+ function refineGateChildEnv(env2 = process.env) {
78464
+ if (env2.VITEST_MAX_WORKERS) return {};
78465
+ const cap = resolveRefineVitestMaxWorkers(env2);
78466
+ if (cap === null) return {};
78467
+ return { VITEST_MAX_WORKERS: String(cap) };
78468
+ }
78469
+ var REFINE_VITEST_MAX_WORKERS_ENV;
78470
+ var DEFAULT_REFINE_VITEST_MAX_WORKERS;
78471
+ var init_mesh_refine_worker_cap = __esm2({
78472
+ "src/mesh/mesh-refine-worker-cap.ts"() {
78473
+ "use strict";
78474
+ REFINE_VITEST_MAX_WORKERS_ENV = "ADHDEV_REFINE_VITEST_MAX_WORKERS";
78475
+ DEFAULT_REFINE_VITEST_MAX_WORKERS = 2;
78476
+ }
78477
+ });
77198
78478
  function truncateValidationOutput(value) {
77199
78479
  const text = typeof value === "string" ? value : value == null ? "" : String(value);
77200
78480
  if (text.length <= REFINE_VALIDATION_SUMMARY_CHARS) return text;
@@ -78673,7 +79953,7 @@ ${mergeTreeErr?.stderr || ""}`;
78673
79953
  encoding: "utf8",
78674
79954
  timeout,
78675
79955
  maxBuffer: candidate.outputLimitBytes || REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
78676
- env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} },
79956
+ env: { ...process.env, CI: process.env.CI || "1", ...refineGateChildEnv(), ...candidate.env || {} },
78677
79957
  ...spawn7.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
78678
79958
  });
78679
79959
  summary.bootstrapCommandsRun.push(commandRecord(candidate, cwd, startedAt, result, true, { exitCode: 0 }));
@@ -78718,7 +79998,7 @@ ${mergeTreeErr?.stderr || ""}`;
78718
79998
  encoding: "utf8",
78719
79999
  timeout,
78720
80000
  maxBuffer: candidate.outputLimitBytes || REFINE_VALIDATION_OUTPUT_LIMIT_BYTES,
78721
- env: { ...process.env, CI: process.env.CI || "1", ...candidate.env || {} },
80001
+ env: { ...process.env, CI: process.env.CI || "1", ...refineGateChildEnv(), ...candidate.env || {} },
78722
80002
  ...spawn7.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
78723
80003
  });
78724
80004
  summary.commandsRun.push(commandRecord(candidate, cwd, startedAt, result, true, { exitCode: 0 }));
@@ -78784,6 +80064,7 @@ ${mergeTreeErr?.stderr || ""}`;
78784
80064
  fs48 = __toESM2(require("fs"));
78785
80065
  import_node_child_process11 = require("child_process");
78786
80066
  init_resolve_executable();
80067
+ init_mesh_refine_worker_cap();
78787
80068
  GIT4 = process.platform === "win32" ? resolveWin32Executable("git") : "git";
78788
80069
  REFINE_VALIDATION_TIMEOUT_MS = 12e4;
78789
80070
  REFINE_VALIDATION_OUTPUT_LIMIT_BYTES = 128 * 1024;
@@ -78869,6 +80150,45 @@ ${mergeTreeErr?.stderr || ""}`;
78869
80150
  REFINE_INFLIGHT_FRESHNESS_MS = 30 * 6e4;
78870
80151
  }
78871
80152
  });
80153
+ function resolveRefineMaxConcurrentJobs(env2 = process.env) {
80154
+ const raw = env2[REFINE_MAX_CONCURRENT_JOBS_ENV];
80155
+ if (raw === void 0 || raw.trim() === "") return DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
80156
+ const parsed = Number.parseInt(raw, 10);
80157
+ if (!Number.isFinite(parsed) || parsed < 1) return DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
80158
+ return parsed;
80159
+ }
80160
+ async function runWithRefineExecutionSlot(label, task) {
80161
+ const limit = resolveRefineMaxConcurrentJobs();
80162
+ if (activeExecutions >= limit) {
80163
+ LOG2.info("Mesh", `[Refinery] ${label} QUEUED \u2014 ${activeExecutions} refine job(s) already executing (limit ${limit}), ${waitQueue.length + 1} waiting. It starts automatically; do not re-invoke.`);
80164
+ await new Promise((resolve30) => {
80165
+ waitQueue.push(resolve30);
80166
+ });
80167
+ LOG2.info("Mesh", `[Refinery] ${label} dequeued \u2014 starting now (${activeExecutions} other job(s) still executing).`);
80168
+ }
80169
+ activeExecutions += 1;
80170
+ try {
80171
+ return await task();
80172
+ } finally {
80173
+ activeExecutions -= 1;
80174
+ const next = waitQueue.shift();
80175
+ if (next) next();
80176
+ }
80177
+ }
80178
+ var REFINE_MAX_CONCURRENT_JOBS_ENV;
80179
+ var DEFAULT_REFINE_MAX_CONCURRENT_JOBS;
80180
+ var activeExecutions;
80181
+ var waitQueue;
80182
+ var init_mesh_refine_concurrency = __esm2({
80183
+ "src/mesh/mesh-refine-concurrency.ts"() {
80184
+ "use strict";
80185
+ init_logger();
80186
+ REFINE_MAX_CONCURRENT_JOBS_ENV = "ADHDEV_REFINE_MAX_CONCURRENT_JOBS";
80187
+ DEFAULT_REFINE_MAX_CONCURRENT_JOBS = 1;
80188
+ activeExecutions = 0;
80189
+ waitQueue = [];
80190
+ }
80191
+ });
78872
80192
  function resolveRefineResumeDispatchGraceMs() {
78873
80193
  return resolveTunedReconcileMs("MESH_REFINE_RESUME_DISPATCH_GRACE_MS", 6e4, 0, 10 * 6e4);
78874
80194
  }
@@ -80807,7 +82127,10 @@ ${e?.stderr || ""}`;
80807
82127
  await appendRefineBatchJobLedger(self, "task_dispatched", handle);
80808
82128
  queueRefineBatchJobEvent(self, "refine:accepted", handle);
80809
82129
  setImmediate(() => {
80810
- void finishMeshRefineBatchJob(self, handle, orderedNodes, ordering, args);
82130
+ void runWithRefineExecutionSlot(
82131
+ `batch ${handle.jobId} (mesh ${meshId})`,
82132
+ () => finishMeshRefineBatchJob(self, handle, orderedNodes, ordering, args)
82133
+ );
80811
82134
  });
80812
82135
  return {
80813
82136
  ...handle,
@@ -81006,7 +82329,10 @@ ${e?.stderr || ""}`;
81006
82329
  queueRefineJobEvent(self, "refine:accepted", handle);
81007
82330
  setImmediate(() => {
81008
82331
  void recordRefineAcceptBaseDivergence(self, handle, node);
81009
- void finishMeshRefineJob(self, handle, args);
82332
+ void runWithRefineExecutionSlot(
82333
+ `job ${handle.jobId} (node ${handle.targetNodeId})`,
82334
+ () => finishMeshRefineJob(self, handle, args)
82335
+ );
81010
82336
  });
81011
82337
  return handle;
81012
82338
  }
@@ -81035,6 +82361,7 @@ ${e?.stderr || ""}`;
81035
82361
  init_git_locale();
81036
82362
  init_mesh_node_identity();
81037
82363
  init_mesh_refine_gates();
82364
+ init_mesh_refine_concurrency();
81038
82365
  init_router_refine_resume();
81039
82366
  RETRYABLE_BASE_MOVEMENT_CODES = /* @__PURE__ */ new Set(["base_moved", "base_locked"]);
81040
82367
  }
@@ -81110,6 +82437,9 @@ ${e?.stderr || ""}`;
81110
82437
  MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
81111
82438
  MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
81112
82439
  MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
82440
+ MESH_GATE_DEFAULT_LEASE_SECONDS: () => MESH_GATE_DEFAULT_LEASE_SECONDS,
82441
+ MESH_GATE_NAMED_OUTCOMES: () => MESH_GATE_NAMED_OUTCOMES,
82442
+ MESH_GATE_RELEASE_PATCH_KEYS: () => MESH_GATE_RELEASE_PATCH_KEYS,
81113
82443
  MESH_JSON_CONFIG_LOCATIONS: () => MESH_JSON_CONFIG_LOCATIONS,
81114
82444
  MESH_JSON_CONFIG_SCHEMA: () => MESH_JSON_CONFIG_SCHEMA,
81115
82445
  MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE: () => MESH_JSON_PROVIDER_DEFAULTS_EXAMPLE,
@@ -81119,16 +82449,21 @@ ${e?.stderr || ""}`;
81119
82449
  MESH_MISSION_LIST_STATUS_LIMIT: () => MESH_MISSION_LIST_STATUS_LIMIT,
81120
82450
  MESH_MISSION_STATUSES: () => MESH_MISSION_STATUSES,
81121
82451
  MESH_NODE_LIVE_TRUTH_MARKER: () => MESH_NODE_LIVE_TRUTH_MARKER,
82452
+ MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT: () => MESH_ON_DEPENDENCY_FAILURE_PUBLIC_TEXT,
81122
82453
  MESH_REFINE_CONFIG_LOCATIONS: () => MESH_REFINE_CONFIG_LOCATIONS,
81123
82454
  MESH_REFINE_CONFIG_SCHEMA: () => MESH_REFINE_CONFIG_SCHEMA,
81124
82455
  MESH_SCHEDULING_STRATEGIES: () => MESH_SCHEDULING_STRATEGIES,
82456
+ MESH_SUPERSEDED_SINGLE_REASONS: () => MESH_SUPERSEDED_SINGLE_REASONS,
81125
82457
  MESH_TASK_GRAPH_MAX_TASKS: () => MESH_TASK_GRAPH_MAX_TASKS,
81126
82458
  MESH_TASK_PRIORITIES: () => MESH_TASK_PRIORITIES,
82459
+ MESH_VALID_SINGLE_REASONS: () => MESH_VALID_SINGLE_REASONS,
81127
82460
  MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_LOCATIONS,
81128
82461
  MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA: () => MESH_WORKTREE_BOOTSTRAP_CONFIG_SCHEMA,
81129
82462
  MIN_GIT_WORKSPACE_POLL_INTERVAL_MS: () => MIN_GIT_WORKSPACE_POLL_INTERVAL_MS,
81130
82463
  MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS: () => MIN_MACHINE_RUNTIME_SUBSCRIPTION_INTERVAL_MS2,
81131
82464
  MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS: () => MIN_SESSION_HOST_DIAGNOSTICS_SUBSCRIPTION_INTERVAL_MS2,
82465
+ MeshGraphPlanError: () => MeshGraphPlanError,
82466
+ MeshGraphPolicyError: () => MeshGraphPolicyError,
81132
82467
  NOT_BEFORE_RELATIVE_THRESHOLD_MS: () => NOT_BEFORE_RELATIVE_THRESHOLD_MS,
81133
82468
  NodePtyTransportFactory: () => NodePtyTransportFactory,
81134
82469
  OPERATING_NOTE_DEDUPE_WINDOW: () => OPERATING_NOTE_DEDUPE_WINDOW,
@@ -81185,6 +82520,7 @@ ${e?.stderr || ""}`;
81185
82520
  buildMeshActiveWork: () => buildMeshActiveWork,
81186
82521
  buildMeshActiveWorkSummary: () => buildMeshActiveWorkSummary,
81187
82522
  buildMeshAsyncRefineJobs: () => buildMeshAsyncRefineJobs,
82523
+ buildMeshGraphViews: () => buildMeshGraphViews,
81188
82524
  buildMeshHostRequiredFailure: () => buildMeshHostRequiredFailure,
81189
82525
  buildMeshJsonConfigScaffold: () => buildMeshJsonConfigScaffold,
81190
82526
  buildMeshLedgerReconciliationEvidence: () => buildMeshLedgerReconciliationEvidence,
@@ -81211,6 +82547,7 @@ ${e?.stderr || ""}`;
81211
82547
  buildUserChatMessage: () => buildUserChatMessage,
81212
82548
  cancelTask: () => cancelTask,
81213
82549
  canonicalDaemonId: () => canonicalDaemonId,
82550
+ claimMeshGraphGate: () => claimMeshGraphGate,
81214
82551
  claimNextTask: () => claimNextTask,
81215
82552
  classifyChatMessageVisibility: () => classifyChatMessageVisibility,
81216
82553
  classifyDuplicateMeshDispatch: () => classifyDuplicateMeshDispatch,
@@ -81225,14 +82562,17 @@ ${e?.stderr || ""}`;
81225
82562
  clearPendingMeshCoordinatorEvents: () => clearPendingMeshCoordinatorEvents,
81226
82563
  collectPendingApprovals: () => collectPendingApprovals,
81227
82564
  collectSyncTargetTypes: () => collectSyncTargetTypes,
82565
+ commitMeshGraphPlan: () => commitMeshGraphPlan,
81228
82566
  compareGitSnapshots: () => compareGitSnapshots,
81229
82567
  compensateWorkspaceIntent: () => compensateWorkspaceIntent,
82568
+ computeMeshGraphPlanDigest: () => computeMeshGraphPlanDigest,
81230
82569
  computeMeshMissionStats: () => computeMeshMissionStats,
81231
82570
  computeMeshTaskStats: () => computeMeshTaskStats,
81232
82571
  computeProviderTreeDigest: () => computeProviderTreeDigest,
81233
82572
  configureDebugTraceStore: () => configureDebugTraceStore,
81234
82573
  connectCdpManager: () => connectCdpManager,
81235
82574
  consoleSymbols: () => consoleSymbols,
82575
+ coordinatorGateBlockReason: () => coordinatorGateBlockReason,
81236
82576
  coordinatorIdentityFromEmitFields: () => coordinatorIdentityFromEmitFields,
81237
82577
  createDebugTraceStore: () => createDebugTraceStore,
81238
82578
  createDefaultGitCommandServices: () => createDefaultGitCommandServices,
@@ -81252,6 +82592,7 @@ ${e?.stderr || ""}`;
81252
82592
  delegatedWorkerAutoApproveSettings: () => delegatedWorkerAutoApproveSettings,
81253
82593
  deleteDirectDispatchesByTaskId: () => deleteDirectDispatchesByTaskId,
81254
82594
  deleteMesh: () => deleteMesh,
82595
+ deriveDependencyFailures: () => deriveDependencyFailures,
81255
82596
  deriveMeshNodeHealthFromGit: () => deriveMeshNodeHealthFromGit,
81256
82597
  deriveMeshReviewInboxItems: () => deriveMeshReviewInboxItems,
81257
82598
  deriveWorkspaceBranchIdentity: () => deriveWorkspaceBranchIdentity,
@@ -81352,6 +82693,7 @@ ${e?.stderr || ""}`;
81352
82693
  installGlobalInterceptor: () => installGlobalInterceptor,
81353
82694
  interactivePromptFromClaudeAskUserQuestion: () => interactivePromptFromClaudeAskUserQuestion,
81354
82695
  isActivityChatMessage: () => isActivityChatMessage,
82696
+ isAdvancedGraphTask: () => isAdvancedGraphTask,
81355
82697
  isAlwaysOnTraceCategory: () => isAlwaysOnTraceCategory,
81356
82698
  isBuiltinChatMessageKind: () => isBuiltinChatMessageKind,
81357
82699
  isCdpConnected: () => isCdpConnected,
@@ -81437,11 +82779,13 @@ ${e?.stderr || ""}`;
81437
82779
  normalizeMeshTaskPriority: () => normalizeMeshTaskPriority,
81438
82780
  normalizeMeshWorkerResult: () => normalizeMeshWorkerResult,
81439
82781
  normalizeMessageParts: () => normalizeMessageParts,
82782
+ normalizeOrchestrationDecision: () => normalizeOrchestrationDecision,
81440
82783
  normalizeRepoIdentity: () => normalizeRepoIdentity,
81441
82784
  normalizeRepoMeshDeclarativeConfig: () => normalizeRepoMeshDeclarativeConfig,
81442
82785
  normalizeSessionModalFields: () => normalizeSessionModalFields,
81443
82786
  notifyCoordinatorOfOrphanedPins: () => notifyCoordinatorOfOrphanedPins,
81444
82787
  otherTrackConfigDir: () => otherTrackConfigDir,
82788
+ parseOnDependencyFailurePolicy: () => parseOnDependencyFailurePolicy,
81445
82789
  parsePorcelainV2Status: () => parsePorcelainV2Status,
81446
82790
  parseProviderSourceConfigUpdate: () => parseProviderSourceConfigUpdate,
81447
82791
  parseWorktreeListOutput: () => parseWorktreeListOutput,
@@ -81458,6 +82802,7 @@ ${e?.stderr || ""}`;
81458
82802
  printQuota: () => printQuota2,
81459
82803
  printQuotaInstallError: () => printQuotaInstallError2,
81460
82804
  probeCdpPort: () => probeCdpPort,
82805
+ projectGraphPublicPolicy: () => projectGraphPublicPolicy,
81461
82806
  pruneOperatingNotes: () => pruneOperatingNotes,
81462
82807
  pruneStaleDirectDispatches: () => pruneStaleDirectDispatches,
81463
82808
  queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
@@ -81481,15 +82826,23 @@ ${e?.stderr || ""}`;
81481
82826
  reconcileDirectDispatchCompletionFromTranscript: () => reconcileDirectDispatchCompletionFromTranscript,
81482
82827
  recordDebugTrace: () => recordDebugTrace,
81483
82828
  recordDirectDispatchTask: () => recordDirectDispatchTask,
82829
+ recordGraphEnqueueCommitted: () => recordGraphEnqueueCommitted,
82830
+ recordGraphEnqueueRolledBack: () => recordGraphEnqueueRolledBack,
82831
+ recordGraphEnqueueValidationFailed: () => recordGraphEnqueueValidationFailed,
82832
+ recordGraphGateClaimed: () => recordGraphGateClaimed,
82833
+ recordGraphGateExpired: () => recordGraphGateExpired,
82834
+ recordGraphGateReleased: () => recordGraphGateReleased,
81484
82835
  recordMeshToolCall: () => recordMeshToolCall,
81485
82836
  recordMissingSessionAttempt: () => recordMissingSessionAttempt2,
81486
82837
  recordSessionUsage: () => recordSessionUsage,
81487
82838
  recoverExpiredWorkspaceSagas: () => recoverExpiredWorkspaceSagas,
81488
82839
  registerExtensionProviders: () => registerExtensionProviders,
81489
82840
  registerMeshCoordinator: () => registerMeshCoordinator,
82841
+ releaseMeshGraphGate: () => releaseMeshGraphGate,
81490
82842
  removeMagiKindPanel: () => removeMagiKindPanel,
81491
82843
  removeNode: () => removeNode,
81492
82844
  removeWorktree: () => removeWorktree,
82845
+ requestUsesGraphV2: () => requestUsesGraphV2,
81493
82846
  requeueHeldMeshCoordinatorEvents: () => requeueHeldMeshCoordinatorEvents,
81494
82847
  requeueTask: () => requeueTask,
81495
82848
  requireMeshHostQueueOwner: () => requireMeshHostQueueOwner,
@@ -81524,6 +82877,7 @@ ${e?.stderr || ""}`;
81524
82877
  resolveNodeSchedulingPriority: () => resolveNodeSchedulingPriority,
81525
82878
  resolveNotBefore: () => resolveNotBefore,
81526
82879
  resolveNpmPublishedVersion: () => resolveNpmPublishedVersion,
82880
+ resolveOnDependencyFailurePolicy: () => resolveOnDependencyFailurePolicy,
81527
82881
  resolveProviderChannel: () => resolveProviderChannel,
81528
82882
  resolveProviderMaxParallel: () => resolveProviderMaxParallel,
81529
82883
  resolveScopedMeshId: () => resolveScopedMeshId,
@@ -81568,6 +82922,7 @@ ${e?.stderr || ""}`;
81568
82922
  summarizeMeshUsage: () => summarizeMeshUsage,
81569
82923
  summarizeMissionTasks: () => summarizeMissionTasks,
81570
82924
  supportsUnicodeSymbols: () => supportsUnicodeSymbols,
82925
+ sweepMeshGraphGateTimeouts: () => sweepMeshGraphGateTimeouts,
81571
82926
  taskDependenciesSatisfied: () => taskDependenciesSatisfied,
81572
82927
  tombstoneOperatingNote: () => tombstoneOperatingNote,
81573
82928
  totalTokens: () => totalTokens,
@@ -84066,10 +85421,667 @@ ${e?.stderr || ""}`;
84066
85421
  };
84067
85422
  }
84068
85423
  init_mesh_work_queue();
85424
+ init_mesh_graph_derived_failure();
84069
85425
  init_mesh_graph_workspace_saga();
84070
85426
  init_mesh_graph_workspace_safety();
84071
85427
  init_mesh_graph_workspace_identity();
84072
85428
  init_mesh_graph_workspace_ports();
85429
+ init_mesh_graph_gates();
85430
+ var import_crypto14 = require("crypto");
85431
+ init_logger();
85432
+ init_mesh_runtime_store();
85433
+ init_mesh_graph_input_binding();
85434
+ init_mesh_graph_types();
85435
+ init_mesh_graph_derived_failure();
85436
+ init_mesh_graph_transition_runner();
85437
+ init_mesh_graph_workspace_saga();
85438
+ init_mesh_work_queue();
85439
+ var MeshGraphPlanError = class extends Error {
85440
+ constructor(code, message, extra) {
85441
+ super(`${code}: ${message}`);
85442
+ this.code = code;
85443
+ this.extra = extra;
85444
+ this.name = "MeshGraphPlanError";
85445
+ }
85446
+ };
85447
+ function isAdvancedGraphTask(spec) {
85448
+ return spec.inputs_from !== void 0 || spec.run_if !== void 0 || typeof spec.workspace_ref === "string" && spec.workspace_ref.trim().length > 0 || Array.isArray(spec.gated_by) && spec.gated_by.length > 0;
85449
+ }
85450
+ function requestUsesGraphV2(req) {
85451
+ if (Array.isArray(req.gates) && req.gates.length > 0) return true;
85452
+ if (Array.isArray(req.workspaces) && req.workspaces.length > 0) return true;
85453
+ return (req.tasks ?? []).some(isAdvancedGraphTask);
85454
+ }
85455
+ function normalizeRef(value, label) {
85456
+ const ref = typeof value === "string" ? value.trim() : "";
85457
+ if (!ref) throw new MeshGraphPlanError("invalid_graph_ref", `${label} requires a non-empty ref`);
85458
+ return ref;
85459
+ }
85460
+ function pickEnum(value, allowed, fallback, code, label) {
85461
+ if (value === void 0 || value === null || value === "") return fallback;
85462
+ if (typeof value === "string" && allowed.includes(value)) return value;
85463
+ throw new MeshGraphPlanError(code, `${label} must be one of ${allowed.join(" | ")} (got ${JSON.stringify(value)})`);
85464
+ }
85465
+ function computeMeshGraphPlanDigest(req) {
85466
+ const normalized = {
85467
+ tasks: (req.tasks ?? []).map((t) => ({
85468
+ ref: typeof t.ref === "string" ? t.ref.trim() : null,
85469
+ message: t.message,
85470
+ depends_on: [...t.dependsOn ?? []].sort(),
85471
+ gated_by: [...t.gated_by ?? []].sort(),
85472
+ inputs_from: t.inputs_from ?? null,
85473
+ run_if: t.run_if ?? null,
85474
+ on_false: t.on_false ?? null,
85475
+ on_upstream_skip: t.on_upstream_skip ?? null,
85476
+ workspace_ref: t.workspace_ref ?? null,
85477
+ target_node_id: t.targetNodeId ?? null,
85478
+ task_mode: t.taskMode ?? null,
85479
+ difficulty: t.difficulty ?? null
85480
+ })),
85481
+ gates: (req.gates ?? []).map((g) => ({
85482
+ ref: g.ref,
85483
+ action: g.action ?? "custom",
85484
+ depends_on: [...g.depends_on ?? []].sort(),
85485
+ on_timeout: g.on_timeout ?? "hold",
85486
+ lease_seconds: g.lease_seconds ?? null,
85487
+ deadline_seconds: g.deadline_seconds ?? null
85488
+ })),
85489
+ workspaces: (req.workspaces ?? []).map((w) => ({
85490
+ ref: w.ref,
85491
+ source_node_id: w.source_node_id ?? null,
85492
+ base_revision: w.base_revision ?? null,
85493
+ desired_path: w.desired_path ?? null,
85494
+ cleanup_on_graph_failure: w.cleanup_on_graph_failure === true
85495
+ })),
85496
+ on_dependency_failure: parseOnDependencyFailurePolicy(req.onDependencyFailure)
85497
+ };
85498
+ return (0, import_crypto14.createHash)("sha256").update(canonicalJson(normalized)).digest("hex");
85499
+ }
85500
+ function commitMeshGraphPlan(req) {
85501
+ const store = MeshRuntimeStore.getInstance();
85502
+ const tasks = Array.isArray(req.tasks) ? req.tasks : [];
85503
+ if (tasks.length === 0) {
85504
+ throw new MeshGraphPlanError("empty_task_graph", "a graph plan requires at least one task");
85505
+ }
85506
+ const gates = Array.isArray(req.gates) ? req.gates : [];
85507
+ const workspaces = Array.isArray(req.workspaces) ? req.workspaces : [];
85508
+ const policy = { on_dependency_failure: parseOnDependencyFailurePolicy(req.onDependencyFailure) };
85509
+ const planDigest = computeMeshGraphPlanDigest(req);
85510
+ const batchId = typeof req.batchId === "string" && req.batchId.trim() ? req.batchId.trim() : newMeshGraphId();
85511
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
85512
+ const taskRefs = /* @__PURE__ */ new Map();
85513
+ tasks.forEach((t, i) => {
85514
+ const ref = typeof t.ref === "string" ? t.ref.trim() : "";
85515
+ if (!ref) return;
85516
+ if (taskRefs.has(ref)) {
85517
+ throw new MeshGraphPlanError("duplicate_task_ref", `ref '${ref}' is used by more than one task in this batch`);
85518
+ }
85519
+ taskRefs.set(ref, i);
85520
+ });
85521
+ const gateRefs = /* @__PURE__ */ new Map();
85522
+ gates.forEach((g, i) => {
85523
+ const ref = normalizeRef(g.ref, `gates[${i}]`);
85524
+ if (taskRefs.has(ref) || gateRefs.has(ref)) {
85525
+ throw new MeshGraphPlanError(
85526
+ "duplicate_graph_ref",
85527
+ `ref '${ref}' is declared more than once \u2014 tasks, gates and workspaces share ONE ref namespace`
85528
+ );
85529
+ }
85530
+ gateRefs.set(ref, i);
85531
+ });
85532
+ const workspaceRefs = /* @__PURE__ */ new Set();
85533
+ workspaces.forEach((w, i) => {
85534
+ const ref = normalizeRef(w.ref, `workspaces[${i}]`);
85535
+ if (taskRefs.has(ref) || gateRefs.has(ref) || workspaceRefs.has(ref)) {
85536
+ throw new MeshGraphPlanError(
85537
+ "duplicate_graph_ref",
85538
+ `ref '${ref}' is declared more than once \u2014 tasks, gates and workspaces share ONE ref namespace`
85539
+ );
85540
+ }
85541
+ workspaceRefs.add(ref);
85542
+ });
85543
+ tasks.forEach((t, i) => {
85544
+ for (const dep of t.dependsOn ?? []) {
85545
+ if (gateRefs.has(dep)) {
85546
+ throw new MeshGraphPlanError(
85547
+ "gate_ref_in_depends_on",
85548
+ `task ${t.ref ? `'${t.ref}'` : `#${i}`} lists gate ref '${dep}' in depends_on \u2014 use gated_by to declare a gate edge (a gate is an intentional stop, never an ordinary queue dependency)`
85549
+ );
85550
+ }
85551
+ }
85552
+ for (const gateRef of t.gated_by ?? []) {
85553
+ if (!gateRefs.has(gateRef)) {
85554
+ throw new MeshGraphPlanError(
85555
+ "unknown_gate_ref",
85556
+ `task ${t.ref ? `'${t.ref}'` : `#${i}`} is gated_by '${gateRef}', which is not a gate in this batch` + (gateRefs.size ? ` (gates: ${[...gateRefs.keys()].join(", ")})` : "")
85557
+ );
85558
+ }
85559
+ }
85560
+ const ws = typeof t.workspace_ref === "string" ? t.workspace_ref.trim() : "";
85561
+ if (ws && !workspaceRefs.has(ws)) {
85562
+ throw new MeshGraphPlanError(
85563
+ "unknown_workspace_ref",
85564
+ `task ${t.ref ? `'${t.ref}'` : `#${i}`} names workspace_ref '${ws}', which is not declared in this batch`
85565
+ );
85566
+ }
85567
+ });
85568
+ gates.forEach((g, i) => {
85569
+ for (const dep of g.depends_on ?? []) {
85570
+ if (!taskRefs.has(dep) && !gateRefs.has(dep)) {
85571
+ throw new MeshGraphPlanError(
85572
+ "unknown_gate_dependency",
85573
+ `gate '${g.ref}' depends on '${dep}', which is not a task or gate ref in this batch`
85574
+ );
85575
+ }
85576
+ }
85577
+ pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", `gates[${i}].action`);
85578
+ pickEnum(g.on_timeout, MESH_GRAPH_GATE_TIMEOUT_POLICIES, "hold", "invalid_gate_on_timeout", `gates[${i}].on_timeout`);
85579
+ });
85580
+ tasks.forEach((t, i) => {
85581
+ pickEnum(t.on_upstream_skip, MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES, "skip", "invalid_on_upstream_skip", `tasks[${i}].on_upstream_skip`);
85582
+ });
85583
+ const result = store.transaction(() => {
85584
+ const graphStore = store.graphStore();
85585
+ const existing = graphStore.getGraphByBatchId(req.meshId, batchId);
85586
+ if (existing) {
85587
+ if (existing.planDigest !== planDigest) {
85588
+ throw new MeshGraphPlanError(
85589
+ "batch_id_conflict",
85590
+ `batch_id '${batchId}' already exists on this mesh with a DIFFERENT plan digest \u2014 reuse of a batch id is only idempotent for an identical plan`,
85591
+ { graphId: existing.graphId }
85592
+ );
85593
+ }
85594
+ const existingNodes = graphStore.listNodes(existing.graphId);
85595
+ const workerNodes = existingNodes.filter((n) => n.kind === "worker_task");
85596
+ const replayTasks = workerNodes.map((n) => n.queueTaskId ? store.findQueueEntryById(req.meshId, n.queueTaskId) : null).filter((e) => !!e);
85597
+ return {
85598
+ graphId: existing.graphId,
85599
+ batchId,
85600
+ tasks: replayTasks,
85601
+ nodeIdByIndex: workerNodes.map((n) => n.nodeId),
85602
+ gates: graphStore.listGatesByGraph(existing.graphId).map((g) => ({
85603
+ ref: g.ref ?? g.gateId,
85604
+ gateId: g.gateId,
85605
+ nodeId: g.nodeId,
85606
+ action: g.action
85607
+ })),
85608
+ workspaces: graphStore.listWorkspaceIntents(existing.graphId).map((w) => ({
85609
+ ref: w.workspaceRef,
85610
+ sagaState: w.sagaState,
85611
+ branchIdentity: w.branchIdentity
85612
+ })),
85613
+ dependencyEdgeCount: existing.dependencyEdgeCount,
85614
+ planDigest,
85615
+ replayed: true,
85616
+ workspacePreparation: existing.workspaceCount > 0 ? "pending" : "not_requested",
85617
+ heldNodeIds: []
85618
+ };
85619
+ }
85620
+ const queueSpecs = tasks.map((t) => {
85621
+ const {
85622
+ inputs_from: _inputsFrom,
85623
+ run_if: _runIf,
85624
+ on_false: _onFalse,
85625
+ on_upstream_skip: _onUpstreamSkip,
85626
+ workspace_ref: _workspaceRef,
85627
+ gated_by: _gatedBy,
85628
+ ...queueSpec
85629
+ } = t;
85630
+ return {
85631
+ ...queueSpec,
85632
+ missionId: queueSpec.missionId ?? req.missionId,
85633
+ ...req.sourceCoordinatorSessionId ? { sourceCoordinatorSessionId: req.sourceCoordinatorSessionId } : {}
85634
+ };
85635
+ });
85636
+ const inserted = enqueueTaskGraph(req.meshId, queueSpecs);
85637
+ const graphId = newMeshGraphId();
85638
+ const nodeIdByIndex = tasks.map(() => newMeshGraphNodeId());
85639
+ const gateNodeIdByRef = /* @__PURE__ */ new Map();
85640
+ const gateIdByRef = /* @__PURE__ */ new Map();
85641
+ gates.forEach((g) => {
85642
+ gateNodeIdByRef.set(g.ref.trim(), newMeshGraphNodeId());
85643
+ gateIdByRef.set(g.ref.trim(), newMeshGraphGateId());
85644
+ });
85645
+ const nodeIdByRef = /* @__PURE__ */ new Map();
85646
+ tasks.forEach((t, i) => {
85647
+ const ref = typeof t.ref === "string" ? t.ref.trim() : "";
85648
+ if (ref) nodeIdByRef.set(ref, nodeIdByIndex[i]);
85649
+ });
85650
+ for (const [ref, nodeId] of gateNodeIdByRef) nodeIdByRef.set(ref, nodeId);
85651
+ const edges = [];
85652
+ const addEdge = (fromNodeId, toNodeId, kind, omitOnSkip) => {
85653
+ edges.push({ graphId, meshId: req.meshId, fromNodeId, toNodeId, kind, omitOnSkip, createdAt: nowIso });
85654
+ };
85655
+ tasks.forEach((t, i) => {
85656
+ const nodeId = nodeIdByIndex[i];
85657
+ const ref = typeof t.ref === "string" ? t.ref.trim() : void 0;
85658
+ const baseSpec = { message: t.message };
85659
+ if (t.inputs_from !== void 0) baseSpec.inputs_from = t.inputs_from;
85660
+ if (t.run_if !== void 0) baseSpec.run_if = t.run_if;
85661
+ if (t.on_false !== void 0) baseSpec.on_false = t.on_false;
85662
+ if (typeof t.workspace_ref === "string" && t.workspace_ref.trim()) {
85663
+ baseSpec.workspace_ref = t.workspace_ref.trim();
85664
+ }
85665
+ const node = {
85666
+ graphId,
85667
+ nodeId,
85668
+ meshId: req.meshId,
85669
+ ...ref ? { ref } : {},
85670
+ kind: "worker_task",
85671
+ queueTaskId: inserted[i].id,
85672
+ state: "declared",
85673
+ baseSpecJson: JSON.stringify(baseSpec),
85674
+ materializationVersion: 0,
85675
+ createdAt: nowIso,
85676
+ updatedAt: nowIso
85677
+ };
85678
+ graphStore.insertNode(node);
85679
+ });
85680
+ gates.forEach((g, i) => {
85681
+ const ref = g.ref.trim();
85682
+ const nodeId = gateNodeIdByRef.get(ref);
85683
+ const gateSpec = {};
85684
+ if (typeof g.lease_seconds === "number" && g.lease_seconds > 0) gateSpec.lease_seconds = g.lease_seconds;
85685
+ if (typeof g.deadline_seconds === "number" && g.deadline_seconds > 0) gateSpec.deadline_seconds = g.deadline_seconds;
85686
+ graphStore.insertNode({
85687
+ graphId,
85688
+ nodeId,
85689
+ meshId: req.meshId,
85690
+ ref,
85691
+ kind: "coordinator_gate",
85692
+ state: "declared",
85693
+ baseSpecJson: JSON.stringify(gateSpec),
85694
+ materializationVersion: 0,
85695
+ createdAt: nowIso,
85696
+ updatedAt: nowIso
85697
+ });
85698
+ graphStore.insertGate({
85699
+ gateId: gateIdByRef.get(ref),
85700
+ graphId,
85701
+ nodeId,
85702
+ meshId: req.meshId,
85703
+ ref,
85704
+ state: "declared",
85705
+ action: pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", `gates[${i}].action`),
85706
+ instructions: typeof g.instructions === "string" ? g.instructions : void 0,
85707
+ eligibleCoordinatorSessionId: typeof g.eligible_coordinator_session_id === "string" ? g.eligible_coordinator_session_id : void 0,
85708
+ leaseGeneration: 0,
85709
+ onTimeout: pickEnum(g.on_timeout, MESH_GRAPH_GATE_TIMEOUT_POLICIES, "hold", "invalid_gate_on_timeout", `gates[${i}].on_timeout`),
85710
+ createdAt: nowIso,
85711
+ updatedAt: nowIso
85712
+ });
85713
+ });
85714
+ tasks.forEach((t, i) => {
85715
+ const omitOnSkip = pickEnum(
85716
+ t.on_upstream_skip,
85717
+ MESH_GRAPH_ON_UPSTREAM_SKIP_POLICIES,
85718
+ "skip",
85719
+ "invalid_on_upstream_skip",
85720
+ `tasks[${i}].on_upstream_skip`
85721
+ ) === "omit_dependency";
85722
+ for (const dep of t.dependsOn ?? []) {
85723
+ const fromNodeId = nodeIdByRef.get(dep);
85724
+ if (!fromNodeId) continue;
85725
+ addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
85726
+ }
85727
+ const gateRefsForNode = new Set((t.gated_by ?? []).map((r) => r.trim()).filter(Boolean));
85728
+ for (const sourceRef of collectRunIfSourceRefs(t.run_if)) {
85729
+ const fromNodeId = nodeIdByRef.get(sourceRef);
85730
+ if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
85731
+ if (gateNodeIdByRef.has(sourceRef)) {
85732
+ gateRefsForNode.add(sourceRef);
85733
+ continue;
85734
+ }
85735
+ if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i])) continue;
85736
+ addEdge(fromNodeId, nodeIdByIndex[i], "conditional", omitOnSkip);
85737
+ }
85738
+ for (const sourceRef of collectInputSourceRefs(t.inputs_from)) {
85739
+ const fromNodeId = nodeIdByRef.get(sourceRef);
85740
+ if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
85741
+ if (gateNodeIdByRef.has(sourceRef)) {
85742
+ gateRefsForNode.add(sourceRef);
85743
+ continue;
85744
+ }
85745
+ if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i] && e.kind === "requires")) continue;
85746
+ addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
85747
+ }
85748
+ for (const gateRef of gateRefsForNode) {
85749
+ const fromNodeId = gateNodeIdByRef.get(gateRef);
85750
+ if (!fromNodeId) continue;
85751
+ addEdge(fromNodeId, nodeIdByIndex[i], "gate", false);
85752
+ }
85753
+ });
85754
+ gates.forEach((g) => {
85755
+ const toNodeId = gateNodeIdByRef.get(g.ref.trim());
85756
+ for (const dep of g.depends_on ?? []) {
85757
+ const fromNodeId = nodeIdByRef.get(dep);
85758
+ if (!fromNodeId) continue;
85759
+ addEdge(fromNodeId, toNodeId, "requires", false);
85760
+ }
85761
+ });
85762
+ for (const edge of edges) graphStore.insertEdge(edge);
85763
+ graphStore.insertGraph({
85764
+ graphId,
85765
+ meshId: req.meshId,
85766
+ batchId,
85767
+ missionId: req.missionId,
85768
+ sourceCoordinatorSessionId: req.sourceCoordinatorSessionId,
85769
+ enqueueSurface: req.enqueueSurface ?? "batch",
85770
+ schemaVersion: MESH_GRAPH_SCHEMA_VERSION,
85771
+ status: "active",
85772
+ taskCount: tasks.length,
85773
+ gateCount: gates.length,
85774
+ workspaceCount: workspaces.length,
85775
+ dependencyEdgeCount: edges.length,
85776
+ planDigest,
85777
+ // ★ E-2: `on_dependency_failure` is now PERSISTED on the graph row
85778
+ // (C3 only validated and echoed it). The C3 derived-failure path reads
85779
+ // it back through parseGraphPolicy/projectGraphPublicPolicy.
85780
+ policyJson: serializeGraphPolicy(policy),
85781
+ createdAt: nowIso,
85782
+ activatedAt: nowIso,
85783
+ updatedAt: nowIso
85784
+ });
85785
+ if (req.orchestrationDecision) {
85786
+ const merged = JSON.stringify({
85787
+ ...JSON.parse(serializeGraphPolicy(policy)),
85788
+ orchestration_decision: req.orchestrationDecision
85789
+ });
85790
+ graphStore.updateGraphPolicyJson(graphId, merged, nowIso);
85791
+ }
85792
+ const heldNodeIds = [];
85793
+ tasks.forEach((t, i) => {
85794
+ if (!isAdvancedGraphTask(t)) return;
85795
+ const nodeId = nodeIdByIndex[i];
85796
+ const entry = store.findQueueEntryById(req.meshId, inserted[i].id);
85797
+ if (!entry || entry.status !== "pending" || entry.blockedReason) return;
85798
+ entry.blockedReason = graphMaterializationBlockReason(nodeId, 0);
85799
+ store.updateQueueEntry(entry);
85800
+ graphStore.updateNodeState(graphId, nodeId, "blocked", nowIso);
85801
+ heldNodeIds.push(nodeId);
85802
+ });
85803
+ return {
85804
+ graphId,
85805
+ batchId,
85806
+ tasks: inserted,
85807
+ nodeIdByIndex,
85808
+ gates: gates.map((g) => ({
85809
+ ref: g.ref.trim(),
85810
+ gateId: gateIdByRef.get(g.ref.trim()),
85811
+ nodeId: gateNodeIdByRef.get(g.ref.trim()),
85812
+ action: pickEnum(g.action, MESH_GRAPH_GATE_ACTIONS, "custom", "invalid_gate_action", "gate action")
85813
+ })),
85814
+ workspaces: [],
85815
+ dependencyEdgeCount: edges.length,
85816
+ planDigest,
85817
+ replayed: false,
85818
+ workspacePreparation: workspaces.length > 0 ? "pending" : "not_requested",
85819
+ heldNodeIds
85820
+ };
85821
+ });
85822
+ if (!result.replayed && workspaces.length > 0) {
85823
+ try {
85824
+ const declared = declareWorkspaceIntents({
85825
+ graphId: result.graphId,
85826
+ meshId: req.meshId,
85827
+ workspaces
85828
+ });
85829
+ result.workspaces = declared.map((w) => ({
85830
+ ref: w.workspaceRef,
85831
+ sagaState: w.sagaState,
85832
+ branchIdentity: w.branchIdentity
85833
+ }));
85834
+ } catch (e) {
85835
+ LOG2.warn("MeshGraph", `Workspace intent declaration failed for graph ${result.graphId}: ${e?.message || e}`);
85836
+ }
85837
+ }
85838
+ return result;
85839
+ }
85840
+ function collectRunIfSourceRefs(runIf, depth = 0) {
85841
+ if (!runIf || typeof runIf !== "object" || depth > 8) return [];
85842
+ const out = [];
85843
+ const node = runIf;
85844
+ if (typeof node.from === "string" && node.from.trim()) out.push(node.from.trim());
85845
+ for (const key2 of ["all", "any", "not", "conditions"]) {
85846
+ const child = node[key2];
85847
+ if (Array.isArray(child)) {
85848
+ for (const item of child) out.push(...collectRunIfSourceRefs(item, depth + 1));
85849
+ } else if (child && typeof child === "object") {
85850
+ out.push(...collectRunIfSourceRefs(child, depth + 1));
85851
+ }
85852
+ }
85853
+ return [...new Set(out)];
85854
+ }
85855
+ function collectInputSourceRefs(inputsFrom) {
85856
+ if (!Array.isArray(inputsFrom)) return [];
85857
+ const out = [];
85858
+ for (const item of inputsFrom) {
85859
+ if (item && typeof item === "object" && typeof item.from === "string") {
85860
+ const from = item.from.trim();
85861
+ if (from) out.push(from);
85862
+ }
85863
+ }
85864
+ return [...new Set(out)];
85865
+ }
85866
+ init_mesh_runtime_store();
85867
+ init_mesh_graph_derived_failure();
85868
+ init_mesh_graph_transition_runner();
85869
+ var IN_FLIGHT_GRAPH_STATUSES = [
85870
+ "preparing",
85871
+ "active",
85872
+ "waiting_gate",
85873
+ "compensation_required"
85874
+ ];
85875
+ function buildMeshGraphViews(meshId, opts = {}) {
85876
+ const store = MeshRuntimeStore.getInstance();
85877
+ const graphStore = store.graphStore();
85878
+ const nowIso = new Date(opts.nowMs ?? Date.now()).toISOString();
85879
+ let graphs;
85880
+ if (opts.graphId) {
85881
+ const one = graphStore.getGraph(opts.graphId);
85882
+ graphs = one && one.meshId === meshId ? [one] : [];
85883
+ } else if (opts.batchId) {
85884
+ const one = graphStore.getGraphByBatchId(meshId, opts.batchId);
85885
+ graphs = one ? [one] : [];
85886
+ } else {
85887
+ graphs = graphStore.listGraphsByMesh(meshId, {
85888
+ ...opts.activeOnly === false ? {} : { statuses: IN_FLIGHT_GRAPH_STATUSES },
85889
+ limit: opts.limit ?? 20
85890
+ });
85891
+ }
85892
+ return graphs.map((graph) => {
85893
+ const nodes = graphStore.listNodes(graph.graphId);
85894
+ const edges = graphStore.listEdges(graph.graphId);
85895
+ const gates = graphStore.listGatesByGraph(graph.graphId);
85896
+ const workspaces = graphStore.listWorkspaceIntents(graph.graphId);
85897
+ const byId = new Map(nodes.map((n) => [n.nodeId, n]));
85898
+ const policy = projectGraphPublicPolicy(graph.policyJson);
85899
+ const decision = readOrchestrationDecision(graph.policyJson);
85900
+ const queueByTaskId = /* @__PURE__ */ new Map();
85901
+ for (const node of nodes) {
85902
+ if (!node.queueTaskId) continue;
85903
+ const entry = store.findQueueEntryById(meshId, node.queueTaskId);
85904
+ if (entry) {
85905
+ queueByTaskId.set(node.queueTaskId, {
85906
+ status: entry.status,
85907
+ blockedReason: entry.blockedReason,
85908
+ cancelReason: entry.cancelReason,
85909
+ dependsOn: entry.dependsOn
85910
+ });
85911
+ }
85912
+ }
85913
+ const statusById = /* @__PURE__ */ new Map();
85914
+ const depMetaById = /* @__PURE__ */ new Map();
85915
+ for (const [taskId, row] of queueByTaskId) {
85916
+ statusById.set(taskId, row.status);
85917
+ depMetaById.set(taskId, { status: row.status, blockedReason: row.blockedReason, cancelReason: row.cancelReason });
85918
+ }
85919
+ for (const row of queueByTaskId.values()) {
85920
+ for (const depId of row.dependsOn ?? []) {
85921
+ if (statusById.has(depId)) continue;
85922
+ const dep = store.findQueueEntryById(meshId, depId);
85923
+ if (!dep) continue;
85924
+ statusById.set(depId, dep.status);
85925
+ depMetaById.set(depId, {
85926
+ status: dep.status,
85927
+ blockedReason: dep.blockedReason,
85928
+ cancelReason: dep.cancelReason
85929
+ });
85930
+ }
85931
+ }
85932
+ const gateByNodeId = new Map(gates.map((g) => [g.nodeId, g]));
85933
+ const nodeViews = nodes.map((node) => {
85934
+ const queue = node.queueTaskId ? queueByTaskId.get(node.queueTaskId) : void 0;
85935
+ const gateBlock = parseCoordinatorGateBlock(queue?.blockedReason);
85936
+ const failures = queue?.dependsOn?.length ? deriveDependencyFailures(queue.dependsOn, statusById, depMetaById) : [];
85937
+ return {
85938
+ nodeId: node.nodeId,
85939
+ ...node.ref ? { ref: node.ref } : {},
85940
+ kind: node.kind,
85941
+ state: node.state,
85942
+ ...node.queueTaskId ? { taskId: node.queueTaskId } : {},
85943
+ ...queue ? { taskStatus: queue.status } : {},
85944
+ ...queue?.blockedReason ? { blockedReason: queue.blockedReason } : {},
85945
+ ...gateBlock ? { blockedByGateId: gateBlock.gateId } : {},
85946
+ materializationVersion: node.materializationVersion,
85947
+ ...node.materializedDigest ? { materializedDigest: node.materializedDigest } : {},
85948
+ ...node.skipReason ? { skipReason: node.skipReason } : {},
85949
+ ...node.failureReason ? { failureReason: node.failureReason } : {},
85950
+ ...failures.length > 0 ? { dependencyFailures: failures } : {},
85951
+ ...describeNodeFeatures(node.baseSpecJson).length > 0 ? { features: describeNodeFeatures(node.baseSpecJson) } : {}
85952
+ };
85953
+ });
85954
+ const edgeViews = edges.map((edge) => {
85955
+ const source = byId.get(edge.fromNodeId);
85956
+ const active = !(source?.state === "skipped" && edge.omitOnSkip);
85957
+ return {
85958
+ from: source?.ref ?? edge.fromNodeId,
85959
+ to: byId.get(edge.toNodeId)?.ref ?? edge.toNodeId,
85960
+ kind: edge.kind,
85961
+ omitOnSkip: edge.omitOnSkip,
85962
+ active
85963
+ };
85964
+ });
85965
+ const gateViews = gates.map((gate) => {
85966
+ const blocking = nodeViews.filter((n) => n.blockedByGateId === gate.gateId).map((n) => n.ref ?? n.nodeId);
85967
+ const leaseExpired = gate.state === "claimed" && !!gate.leaseExpiresAt && gate.leaseExpiresAt <= nowIso;
85968
+ return {
85969
+ gateId: gate.gateId,
85970
+ ...gate.ref ? { ref: gate.ref } : {},
85971
+ nodeId: gate.nodeId,
85972
+ state: gate.state,
85973
+ action: gate.action,
85974
+ ...gate.instructions ? { instructions: gate.instructions } : {},
85975
+ onTimeout: gate.onTimeout,
85976
+ leaseGeneration: gate.leaseGeneration,
85977
+ ...gate.leaseOwnerSessionId ? { leaseOwnerSessionId: gate.leaseOwnerSessionId } : {},
85978
+ ...gate.leaseExpiresAt ? { leaseExpiresAt: gate.leaseExpiresAt } : {},
85979
+ ...leaseExpired ? { leaseExpired: true } : {},
85980
+ ...gate.deadlineAt ? { deadlineAt: gate.deadlineAt } : {},
85981
+ ...gate.releaseOutcome ? { releaseOutcome: gate.releaseOutcome } : {},
85982
+ ...gate.eligibleCoordinatorSessionId ? { eligibleCoordinatorSessionId: gate.eligibleCoordinatorSessionId } : {},
85983
+ ...blocking.length > 0 ? { blocking } : {}
85984
+ };
85985
+ });
85986
+ const nodeStates = {};
85987
+ for (const node of nodes) nodeStates[node.state] = (nodeStates[node.state] ?? 0) + 1;
85988
+ const actions = [];
85989
+ for (const gate of gateViews) {
85990
+ if (gate.state === "awaiting_coordinator") {
85991
+ actions.push({
85992
+ kind: "gate_awaiting",
85993
+ gateId: gate.gateId,
85994
+ detail: `Gate '${gate.ref ?? gate.gateId}' (${gate.action}) is awaiting a coordinator. Claim it with mesh_graph_gate_claim, perform the action, then release it with mesh_graph_gate_release.`
85995
+ });
85996
+ } else if (gate.state === "claimed" && gate.leaseExpired) {
85997
+ actions.push({
85998
+ kind: "gate_reclaim",
85999
+ gateId: gate.gateId,
86000
+ detail: `Gate '${gate.ref ?? gate.gateId}' has a LAPSED lease (generation ${gate.leaseGeneration}). Reclaim it to get a higher generation; the previous owner may already have performed the external side effect, so reconcile external evidence before acting again.`
86001
+ });
86002
+ } else if (gate.state === "claimed") {
86003
+ actions.push({
86004
+ kind: "gate_claimed",
86005
+ gateId: gate.gateId,
86006
+ detail: `Gate '${gate.ref ?? gate.gateId}' is claimed by ${gate.leaseOwnerSessionId ?? "a coordinator"} until ${gate.leaseExpiresAt ?? "lease expiry"}; release it to advance the graph.`
86007
+ });
86008
+ } else if (gate.state === "expired" && gate.onTimeout === "hold") {
86009
+ actions.push({
86010
+ kind: "gate_reclaim",
86011
+ gateId: gate.gateId,
86012
+ detail: `Gate '${gate.ref ?? gate.gateId}' passed its deadline under on_timeout=hold \u2014 downstream is still held. Reclaim it (optionally with extend_deadline_seconds) to resume, or cancel the branch explicitly. A timeout is never passage: the gate cannot release itself.`
86013
+ });
86014
+ }
86015
+ }
86016
+ for (const ws of workspaces) {
86017
+ if (ws.sagaState === "compensation_required") {
86018
+ actions.push({
86019
+ kind: "workspace_compensation",
86020
+ workspaceRef: ws.workspaceRef,
86021
+ detail: `Workspace '${ws.workspaceRef}' needs manual compensation: ${ws.lastError ?? "compensation refused"}.`
86022
+ });
86023
+ }
86024
+ }
86025
+ return {
86026
+ graphId: graph.graphId,
86027
+ batchId: graph.batchId,
86028
+ status: graph.status,
86029
+ ...graph.missionId ? { missionId: graph.missionId } : {},
86030
+ enqueueSurface: graph.enqueueSurface,
86031
+ schemaVersion: graph.schemaVersion,
86032
+ createdAt: graph.createdAt,
86033
+ ...graph.terminalAt ? { terminalAt: graph.terminalAt } : {},
86034
+ onDependencyFailure: policy.on_dependency_failure,
86035
+ ...policy.policyInvalid ? { policyInvalid: policy.policyInvalid } : {},
86036
+ ...decision ? { orchestrationDecision: decision } : {},
86037
+ counts: {
86038
+ tasks: graph.taskCount,
86039
+ gates: graph.gateCount,
86040
+ workspaces: graph.workspaceCount,
86041
+ edges: graph.dependencyEdgeCount
86042
+ },
86043
+ nodeStates,
86044
+ nodes: nodeViews,
86045
+ edges: edgeViews,
86046
+ gates: gateViews,
86047
+ workspaces: workspaces.map((w) => ({
86048
+ ref: w.workspaceRef,
86049
+ sagaState: w.sagaState,
86050
+ ...w.branchIdentity ? { branchIdentity: w.branchIdentity } : {},
86051
+ ...w.createdNodeId ? { createdNodeId: w.createdNodeId } : {},
86052
+ ...w.createdWorktreePath ? { createdWorktreePath: w.createdWorktreePath } : {},
86053
+ ...w.lastError ? { lastError: w.lastError } : {}
86054
+ })),
86055
+ ...actions.length > 0 ? { nextCoordinatorAction: actions } : {}
86056
+ };
86057
+ });
86058
+ }
86059
+ function describeNodeFeatures(baseSpecJson) {
86060
+ let spec;
86061
+ try {
86062
+ const parsed = JSON.parse(baseSpecJson);
86063
+ spec = parsed && typeof parsed === "object" ? parsed : void 0;
86064
+ } catch {
86065
+ return ["invalid_base_spec"];
86066
+ }
86067
+ if (!spec) return [];
86068
+ const features = [];
86069
+ if (Array.isArray(spec.inputs_from) && spec.inputs_from.length > 0) features.push("inputs_from");
86070
+ if (spec.run_if !== void 0) features.push("run_if");
86071
+ if (typeof spec.workspace_ref === "string" && spec.workspace_ref) features.push("workspace_ref");
86072
+ return features;
86073
+ }
86074
+ function readOrchestrationDecision(policyJson) {
86075
+ if (!policyJson) return void 0;
86076
+ try {
86077
+ const parsed = JSON.parse(policyJson);
86078
+ const decision = parsed?.orchestration_decision;
86079
+ return decision && typeof decision === "object" && !Array.isArray(decision) ? decision : void 0;
86080
+ } catch {
86081
+ return void 0;
86082
+ }
86083
+ }
86084
+ init_mesh_graph_provenance();
84073
86085
  init_mesh_node_identity();
84074
86086
  init_mesh_active_work();
84075
86087
  init_mesh_idle_reminder();
@@ -111685,7 +113697,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
111685
113697
  var path47 = __toESM2(require("path"));
111686
113698
  var fs41 = __toESM2(require("fs"));
111687
113699
  var path46 = __toESM2(require("path"));
111688
- var import_crypto14 = require("crypto");
113700
+ var import_crypto16 = require("crypto");
111689
113701
  var TREE_DIGEST_ALGORITHM = "adhdev-provider-tree-sha256-v1";
111690
113702
  function computeProviderTreeDigest(rootDir, providerType) {
111691
113703
  const relPaths = [];
@@ -111698,7 +113710,7 @@ Run 'adhdev doctor' for detailed diagnostics.`
111698
113710
  );
111699
113711
  }
111700
113712
  relPaths.sort();
111701
- const hash2 = (0, import_crypto14.createHash)("sha256");
113713
+ const hash2 = (0, import_crypto16.createHash)("sha256");
111702
113714
  for (const relPath of relPaths) {
111703
113715
  const absPath = path46.join(rootDir, ...relPath.split("/"));
111704
113716
  const bytes = fs41.readFileSync(absPath);
@@ -115775,11 +117787,11 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
115775
117787
  let node;
115776
117788
  const { migrateProviderRolesToSlots: migrateProviderRolesToSlots2 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
115777
117789
  if (meshRecord.inline) {
115778
- const { randomUUID: randomUUID19 } = await import("crypto");
117790
+ const { randomUUID: randomUUID20 } = await import("crypto");
115779
117791
  const clonedPolicy = { ...sourceNode.policy || {} };
115780
117792
  migrateProviderRolesToSlots2(clonedPolicy);
115781
117793
  node = {
115782
- id: `node_${randomUUID19().replace(/-/g, "")}`,
117794
+ id: `node_${randomUUID20().replace(/-/g, "")}`,
115783
117795
  workspace: result.worktreePath,
115784
117796
  repoRoot: result.worktreePath,
115785
117797
  daemonId: sourceNode.daemonId,
@@ -116311,8 +118323,10 @@ ${formatManifestValidationIssues2(validation2.issues)}`);
116311
118323
  const { getMeshQueueStats: getMeshQueueStats2, getQueue: getQueue2, describeTaskDependencyState: describeTaskDependencyState2 } = await Promise.resolve().then(() => (init_mesh_work_queue(), mesh_work_queue_exports));
116312
118324
  const status = Array.isArray(args?.status) ? args.status.map((s2) => typeof s2 === "string" ? s2.trim() : "").filter(Boolean) : void 0;
116313
118325
  const rawQueue = getQueue2(meshId, { status });
116314
- const statusById = new Map(getQueue2(meshId).map((task) => [task.id, task.status]));
116315
- const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById) } : task);
118326
+ const allQueue = getQueue2(meshId);
118327
+ const statusById = new Map(allQueue.map((task) => [task.id, task.status]));
118328
+ const depMetaById = new Map(allQueue.map((task) => [task.id, task]));
118329
+ const queue = rawQueue.map((task) => Array.isArray(task.dependsOn) && task.dependsOn.length > 0 ? { ...task, ...describeTaskDependencyState2(task, statusById, depMetaById) } : task);
116316
118330
  const summary = getMeshQueueStats2(meshId);
116317
118331
  return {
116318
118332
  success: true,
@@ -127391,7 +129405,7 @@ data: ${JSON.stringify(msg.data)}
127391
129405
  });
127392
129406
  }
127393
129407
  };
127394
- var import_crypto15 = require("crypto");
129408
+ var import_crypto17 = require("crypto");
127395
129409
  var import_session_host_core12 = require_dist();
127396
129410
  var BASE_KEY_SEQUENCES = {
127397
129411
  enter: "\r",
@@ -127489,7 +129503,7 @@ data: ${JSON.stringify(msg.data)}
127489
129503
  const sessionId = String(options.sessionId || "").trim();
127490
129504
  if (!sessionId) throw new Error("sessionId is required");
127491
129505
  const mode = options.mode || "read";
127492
- const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto15.randomUUID)().slice(0, 8)}`;
129506
+ const clientId = options.clientId || `raw-terminal-${process.pid}-${(0, import_crypto17.randomUUID)().slice(0, 8)}`;
127493
129507
  const client = options.client || new import_session_host_core12.SessionHostClient({ endpoint: options.endpoint });
127494
129508
  await client.connect();
127495
129509
  const attachResponse = await client.request({
@@ -128253,7 +130267,7 @@ data: ${JSON.stringify(msg.data)}
128253
130267
  init_mesh_reconcile_loop();
128254
130268
  var import_fs19 = require("fs");
128255
130269
  var import_path20 = require("path");
128256
- var import_crypto16 = require("crypto");
130270
+ var import_crypto18 = require("crypto");
128257
130271
  init_logger();
128258
130272
  init_mesh_ledger();
128259
130273
  init_mesh_runtime_store();
@@ -128287,7 +130301,7 @@ data: ${JSON.stringify(msg.data)}
128287
130301
  try {
128288
130302
  const fingerprint = buildPendingEventFingerprint(event);
128289
130303
  MeshRuntimeStore.getInstance().insertPendingEvent({
128290
- id: (0, import_crypto16.randomUUID)(),
130304
+ id: (0, import_crypto18.randomUUID)(),
128291
130305
  meshId: event.meshId,
128292
130306
  coordinatorDaemonId: event.targetCoordinatorDaemonId ?? null,
128293
130307
  event: event.event,
@@ -128408,6 +130422,73 @@ data: ${JSON.stringify(msg.data)}
128408
130422
  }
128409
130423
  return newlyFrozen;
128410
130424
  }
130425
+ var import_node_perf_hooks = require("perf_hooks");
130426
+ init_logger();
130427
+ var EVENT_LOOP_MONITOR_INTERVAL_ENV = "ADHDEV_EVENT_LOOP_MONITOR_INTERVAL_MS";
130428
+ var EVENT_LOOP_LAG_WARN_ENV = "ADHDEV_EVENT_LOOP_LAG_WARN_MS";
130429
+ var DEFAULT_EVENT_LOOP_MONITOR_INTERVAL_MS = 1e4;
130430
+ var DEFAULT_EVENT_LOOP_LAG_WARN_MS = 5e3;
130431
+ function parseMs(raw, fallback) {
130432
+ if (raw === void 0 || raw.trim() === "") return fallback;
130433
+ const parsed = Number.parseInt(raw, 10);
130434
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
130435
+ }
130436
+ function resolveEventLoopMonitorIntervalMs(env2 = process.env) {
130437
+ return parseMs(env2[EVENT_LOOP_MONITOR_INTERVAL_ENV], DEFAULT_EVENT_LOOP_MONITOR_INTERVAL_MS);
130438
+ }
130439
+ function resolveEventLoopLagWarnMs(env2 = process.env) {
130440
+ return parseMs(env2[EVENT_LOOP_LAG_WARN_ENV], DEFAULT_EVENT_LOOP_LAG_WARN_MS);
130441
+ }
130442
+ function defaultLogFn(level, message) {
130443
+ if (level === "warn") LOG2.warn("EventLoop", message);
130444
+ else LOG2.debug("EventLoop", message);
130445
+ }
130446
+ function startEventLoopMonitor(opts) {
130447
+ const intervalMs = opts?.intervalMs ?? resolveEventLoopMonitorIntervalMs();
130448
+ const warnMs = opts?.warnMs ?? resolveEventLoopLagWarnMs();
130449
+ const logFn = opts?.logFn ?? defaultLogFn;
130450
+ if (intervalMs <= 0) return { stop: () => {
130451
+ } };
130452
+ let histogram;
130453
+ try {
130454
+ histogram = (0, import_node_perf_hooks.monitorEventLoopDelay)({ resolution: 20 });
130455
+ histogram.enable();
130456
+ } catch {
130457
+ return { stop: () => {
130458
+ } };
130459
+ }
130460
+ let expectedTickAt = null;
130461
+ const timer = setInterval(() => {
130462
+ try {
130463
+ const now = Date.now();
130464
+ const driftMs = expectedTickAt === null ? 0 : Math.max(0, now - expectedTickAt);
130465
+ expectedTickAt = now + intervalMs;
130466
+ const maxMs = histogram.max / 1e6;
130467
+ const p99Ms = histogram.percentile(99) / 1e6;
130468
+ const meanMs = histogram.mean / 1e6;
130469
+ histogram.reset();
130470
+ const worstMs = Math.max(driftMs, maxMs);
130471
+ const message = `event-loop lag: drift=${driftMs.toFixed(0)}ms max=${maxMs.toFixed(0)}ms p99=${p99Ms.toFixed(0)}ms mean=${meanMs.toFixed(1)}ms over ${intervalMs}ms window`;
130472
+ if (worstMs >= warnMs) {
130473
+ logFn("warn", `${message} \u2014 EXCEEDS ${warnMs}ms: the whole daemon process was frozen/unscheduled (machine saturation), not one slow handler. Expect IPC probe timeouts and reconcile-tick gaps in this window.`);
130474
+ } else {
130475
+ logFn("debug", message);
130476
+ }
130477
+ } catch {
130478
+ }
130479
+ }, intervalMs);
130480
+ expectedTickAt = Date.now() + intervalMs;
130481
+ timer.unref?.();
130482
+ return {
130483
+ stop: () => {
130484
+ clearInterval(timer);
130485
+ try {
130486
+ histogram.disable();
130487
+ } catch {
130488
+ }
130489
+ }
130490
+ };
130491
+ }
128411
130492
  init_require_whitelist();
128412
130493
  async function initDaemonComponents2(config2) {
128413
130494
  applyProcessHardening();
@@ -128707,6 +130788,7 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
128707
130788
  refreshQuotaCacheOnBoot(isQuotaProviderEnabled);
128708
130789
  });
128709
130790
  setImmediate(() => void router.resumePendingRefineJobsOnStartup());
130791
+ components.eventLoopMonitor = startEventLoopMonitor();
128710
130792
  setImmediate(() => router.resumeDeferredRestartsOnStartup());
128711
130793
  return components;
128712
130794
  }
@@ -128736,10 +130818,15 @@ ${upgradeFailureNotice.notice}${supersededHint}`);
128736
130818
  meshReconcileLoop,
128737
130819
  quotaRefreshLoop,
128738
130820
  quotaEventRefresh,
128739
- providerStalenessProbe
130821
+ providerStalenessProbe,
130822
+ eventLoopMonitor
128740
130823
  } = components;
128741
130824
  poller.stop();
128742
130825
  cdpInitializer.stop();
130826
+ try {
130827
+ eventLoopMonitor?.stop();
130828
+ } catch {
130829
+ }
128743
130830
  try {
128744
130831
  meshReconcileLoop?.stop();
128745
130832
  } catch {