@adhdev/daemon-standalone 1.0.56-rc.4 → 1.0.56-rc.6

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
@@ -36620,10 +36620,10 @@ var require_dist3 = __commonJS({
36620
36620
  }
36621
36621
  function getDaemonBuildInfo() {
36622
36622
  if (cached2) return cached2;
36623
- const commit = readInjected(true ? "0ac5de5a5c119ce79644b72b5cf2e44ae1afc677" : void 0) ?? "unknown";
36624
- const commitShort = readInjected(true ? "0ac5de5a" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36625
- const version2 = readInjected(true ? "1.0.56-rc.4" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36626
- const builtAt = readInjected(true ? "2026-08-19T02:15:04.773Z" : void 0);
36623
+ const commit = readInjected(true ? "d143fc7369d10b184b4cad79ce3a4240bc811faf" : void 0) ?? "unknown";
36624
+ const commitShort = readInjected(true ? "d143fc73" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
36625
+ const version2 = readInjected(true ? "1.0.56-rc.6" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
36626
+ const builtAt = readInjected(true ? "2026-08-19T09:46:30.077Z" : void 0);
36627
36627
  cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
36628
36628
  return cached2;
36629
36629
  }
@@ -42852,8 +42852,9 @@ ${error48.message || ""}`;
42852
42852
  CANONICAL_MESH_TOOL_NAMES = [
42853
42853
  "mesh_status",
42854
42854
  "mesh_list_nodes",
42855
- "mesh_enqueue_task",
42855
+ // GRAPH-ORCHESTRATION Phase F — batch before task, mirroring ALL_MESH_TOOLS.
42856
42856
  "mesh_enqueue_batch",
42857
+ "mesh_enqueue_task",
42857
42858
  "mesh_view_queue",
42858
42859
  // GRAPH-ORCHESTRATION Phase E — the coordinator gate + graph view surface.
42859
42860
  "mesh_graph_view",
@@ -52427,62 +52428,6 @@ ${blocks.join("\n\n")}`;
52427
52428
  MAX_CLONE_CHAIN = 16;
52428
52429
  }
52429
52430
  });
52430
- function loadDatabaseCtor() {
52431
- if (DatabaseCtor) return DatabaseCtor;
52432
- DatabaseCtor = loadBetterSqlite3();
52433
- return DatabaseCtor;
52434
- }
52435
- function safeMeshId(meshId) {
52436
- return meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
52437
- }
52438
- function legacyQueuePath(meshId) {
52439
- return (0, import_path6.join)(getLedgerDir2(), `${safeMeshId(meshId)}.queue.json`);
52440
- }
52441
- function cleanupStrayRootRuntimeDb(canonicalPath2) {
52442
- try {
52443
- const strayPath = (0, import_path6.join)(getConfigDir2(), "mesh-runtime.db");
52444
- if (strayPath === canonicalPath2) return;
52445
- if (!(0, import_fs6.existsSync)(strayPath)) return;
52446
- if ((0, import_fs6.statSync)(strayPath).size !== 0) return;
52447
- (0, import_fs6.unlinkSync)(strayPath);
52448
- if (!loggedStrayCleanup) {
52449
- loggedStrayCleanup = true;
52450
- LOG2.info("MeshRuntimeStore", `Removed stray 0-byte root mesh-runtime.db at ${strayPath}`);
52451
- }
52452
- } catch (err) {
52453
- if (!loggedStrayCleanup) {
52454
- loggedStrayCleanup = true;
52455
- LOG2.warn("MeshRuntimeStore", `Stray root mesh-runtime.db cleanup failed (ignored): ${err?.message || err}`);
52456
- }
52457
- }
52458
- }
52459
- function meshRuntimeStorePath() {
52460
- const dir = getLedgerDir2();
52461
- const nextPath = (0, import_path6.join)(dir, "mesh-runtime.db");
52462
- cleanupStrayRootRuntimeDb(nextPath);
52463
- if ((0, import_fs6.existsSync)(nextPath)) return nextPath;
52464
- const legacyPath = (0, import_path6.join)(dir, "beads.db");
52465
- if (!(0, import_fs6.existsSync)(legacyPath)) return nextPath;
52466
- try {
52467
- (0, import_fs6.renameSync)(legacyPath, nextPath);
52468
- for (const suffix of ["-wal", "-shm"]) {
52469
- const legacyCompanion = `${legacyPath}${suffix}`;
52470
- if ((0, import_fs6.existsSync)(legacyCompanion)) {
52471
- (0, import_fs6.renameSync)(legacyCompanion, `${nextPath}${suffix}`);
52472
- }
52473
- }
52474
- } catch (err) {
52475
- if (!loggedMigrationFailure) {
52476
- loggedMigrationFailure = true;
52477
- LOG2.warn(
52478
- "MeshRuntimeStore",
52479
- `Legacy beads.db\u2192mesh-runtime.db migration failed; using existing DB in-place to avoid data loss: ${err?.message || err}`
52480
- );
52481
- }
52482
- return (0, import_fs6.existsSync)(nextPath) ? nextPath : legacyPath;
52483
- }
52484
- return nextPath;
52485
- }
52486
52431
  function meshTurnAttemptFromRow(r) {
52487
52432
  return {
52488
52433
  attemptId: r.attempt_id,
@@ -52540,15 +52485,82 @@ ${blocks.join("\n\n")}`;
52540
52485
  return { ledger: 0, toolCalls: 0, terminalQueue: 0, sessionDelivery: 0 };
52541
52486
  }
52542
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
+ }
52543
52558
  var import_fs6;
52544
52559
  var import_path6;
52545
52560
  var DatabaseCtor;
52546
52561
  var loggedMigrationFailure;
52547
52562
  var loggedStrayCleanup;
52548
52563
  var MeshRuntimeStore;
52549
- var MESH_EVENT_LEDGER_RETENTION_MS;
52550
- var MESH_TOOL_CALL_LOG_RETENTION_MS;
52551
- var MESH_TERMINAL_QUEUE_RETENTION_MS;
52552
52564
  var init_mesh_runtime_store = __esm2({
52553
52565
  "src/mesh/mesh-runtime-store.ts"() {
52554
52566
  "use strict";
@@ -52558,13 +52570,14 @@ ${blocks.join("\n\n")}`;
52558
52570
  init_load_better_sqlite3();
52559
52571
  init_config();
52560
52572
  init_mesh_ledger();
52561
- init_mesh_retention_config();
52562
52573
  init_mesh_work_queue();
52563
52574
  init_mesh_graph_schema();
52564
52575
  init_mesh_graph_store();
52565
52576
  init_slot_model_enforcement();
52566
52577
  init_mesh_daemon_slot_axis();
52567
52578
  init_dist();
52579
+ init_mesh_runtime_store_turn_rows();
52580
+ init_mesh_runtime_store_turn_rows();
52568
52581
  loggedMigrationFailure = false;
52569
52582
  loggedStrayCleanup = false;
52570
52583
  MeshRuntimeStore = class _MeshRuntimeStore {
@@ -52760,6 +52773,7 @@ ${blocks.join("\n\n")}`;
52760
52773
  mesh_id TEXT NOT NULL,
52761
52774
  tool TEXT NOT NULL,
52762
52775
  session_id TEXT,
52776
+ caller_role TEXT,
52763
52777
  called_at INTEGER NOT NULL
52764
52778
  );
52765
52779
 
@@ -53089,6 +53103,10 @@ ${blocks.join("\n\n")}`;
53089
53103
  ON mesh_event_ledger(mesh_id, task_id, timestamp)
53090
53104
  WHERE task_id IS NOT NULL
53091
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
+ }
53092
53110
  } catch (err) {
53093
53111
  if (!loggedMigrationFailure) {
53094
53112
  loggedMigrationFailure = true;
@@ -54022,16 +54040,23 @@ ${blocks.join("\n\n")}`;
54022
54040
  * Returns a rate-limit advisory string when the call rate is too high, null otherwise.
54023
54041
  * windowMs: sliding window size in ms (default 10s)
54024
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.
54025
54050
  */
54026
54051
  recordMeshToolCall(opts) {
54027
- const { meshId, tool, sessionId = null } = opts;
54052
+ const { meshId, tool, sessionId = null, callerRole = null } = opts;
54028
54053
  const windowMs = opts.windowMs ?? 1e4;
54029
54054
  const maxCalls = opts.maxCalls ?? 5;
54030
54055
  const now = Date.now();
54031
54056
  const windowStart = now - windowMs;
54032
54057
  this.db.prepare(
54033
- "INSERT INTO mesh_tool_call_log (mesh_id, tool, session_id, called_at) VALUES (?, ?, ?, ?)"
54034
- ).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);
54035
54060
  const row = this.db.prepare(
54036
54061
  "SELECT COUNT(*) as cnt FROM mesh_tool_call_log WHERE mesh_id = ? AND tool = ? AND called_at >= ?"
54037
54062
  ).get(meshId, tool, windowStart);
@@ -54057,6 +54082,18 @@ ${blocks.join("\n\n")}`;
54057
54082
  pruneToolCallLog(olderThanMs) {
54058
54083
  return this.db.prepare("DELETE FROM mesh_tool_call_log WHERE called_at < ?").run(Date.now() - olderThanMs).changes;
54059
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
+ }
54060
54097
  /**
54061
54098
  * Retention prune for mesh_event_ledger (SoT 1-11 (b)). The ledger is append-only
54062
54099
  * with NO lifecycle GC of its own, so lifecycle events accumulate without bound
@@ -55199,9 +55236,6 @@ ${blocks.join("\n\n")}`;
55199
55236
  return res.changes > 0;
55200
55237
  }
55201
55238
  };
55202
- MESH_EVENT_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
55203
- MESH_TOOL_CALL_LOG_RETENTION_MS = 14 * 24 * 60 * 60 * 1e3;
55204
- MESH_TERMINAL_QUEUE_RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
55205
55239
  }
55206
55240
  });
55207
55241
  var mesh_ledger_exports = {};
@@ -56755,7 +56789,7 @@ Default branch: \`${mesh.defaultBranch}\`` : ""}`);
56755
56789
  const lines = [
56756
56790
  "## Task difficulty",
56757
56791
  "",
56758
- "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.",
56759
56793
  "",
56760
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.'
56761
56795
  ];
@@ -56839,11 +56873,12 @@ ${rules.join("\n")}`;
56839
56873
  return `## Rules
56840
56874
 
56841
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}
56842
- - **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.
56843
- - **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\` (the default \u2014 see Workflow 3.a), falling back to \`mesh_enqueue_task\` only for a terminal single step, to \`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 whichever dispatch surface Workflow 3.a selects (\`mesh_enqueue_batch\` by default; \`mesh_enqueue_task\` / \`mesh_send_task\` in their narrower cases). 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\`.
56844
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.
56845
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.
56846
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.
56881
+ - **Batch is the default enqueue surface.** Apply Workflow 3.a: before every enqueue, ask whether you will read this task's result and then dispatch more work. If yes, that successor is declarable \u2014 submit the whole plan as one \`mesh_enqueue_batch\` with \`inputs_from\` / \`run_if\` / \`gated_by\`, rather than enqueueing a step, waiting, and enqueueing the next by hand. \`mesh_enqueue_task\` is the fallback for a genuinely terminal single step, and it takes an \`orchestration_decision\` stating which \`single_reason\` applies.
56847
56882
  - **Base nodes are reserved for environment-specific testing.** Apply Workflow 3.b0: only work that verifies a machine's physical environment runs on a base node (pinned with \`required_tags\`/\`target_node_id\`); every ordinary \`code_change\` gets its own cloned worktree. Node availability is not branch isolation.
56848
56883
  - **Worktree affinity.** Apply Workflow 3.b1: route a branch's follow-ups back to its own worktree node (\`required_tags: ["worktree=<branch>"]\` or \`target_node_id\`, taken from the \`mesh_clone_node\` result or a live \`mesh_status\`); only \`convergence\` (merge/push) runs base-side.
56849
56884
  - **Classify task difficulty honestly.** Judge each task's real difficulty (\`easy\`/\`medium\`/\`difficult\`/\`freeform\`) per the Task difficulty section above \u2014 it is a routing hint, and the matched slot's own model/thinking is what launches. Never bend difficulty to chase a model; retune slots instead (\`mesh_node_slots_set\`).
@@ -56904,12 +56939,12 @@ When you compose the task message you dispatch to a node, include this requireme
56904
56939
  |------|---------|
56905
56940
  | \`mesh_status\` | Nodes' health, git state, sessions, branch convergence |
56906
56941
  | \`mesh_list_nodes\` | List nodes with workspace paths |
56907
- | \`mesh_enqueue_task\` | Add a task to the pull-based queue; idle nodes auto-claim |
56908
- | \`mesh_enqueue_batch\` | Atomically enqueue a dependency-wired task set; \`depends_on\` may name batch-local \`ref\`s (forward refs OK). Also carries the full graph surface: \`inputs_from\`, \`run_if\`, \`gates\` + \`gated_by\`, \`workspaces\` + \`workspace_ref\` |
56942
+ | \`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\` |
56943
+ | \`mesh_enqueue_task\` | SINGLE-TASK FALLBACK \u2014 one ready task with no declarable downstream step; idle nodes auto-claim |
56909
56944
  | \`mesh_view_queue\` | Queue status \u2014 pending/assigned/completed/failed/cancelled |
56910
56945
  | \`mesh_graph_view\` | Inspect orchestration graphs \u2014 node states, gates awaiting you, workspace sagas, why something is blocked |
56911
56946
  | \`mesh_graph_gate_claim\` | Take the lease on a gate awaiting a coordinator; returns the fencing token + generation a release needs |
56912
- | \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate) |
56947
+ | \`mesh_graph_gate_release\` | Pass a gate you hold \u2014 the ONLY way through one (no timeout ever passes a gate). **A gate and its dependents are one unit**: a gate earns its keep only when some task names it in \`gated_by\`, because releasing it is what dispatches that task. A gate nothing depends on opens nothing and is pure claim/release overhead \u2014 declare the follower in the same batch, or skip the gate |
56913
56948
  | \`mesh_queue_cancel\` | Cancel a queue task (audit history kept) |
56914
56949
  | \`mesh_queue_requeue\` | Return a task to pending for retry |
56915
56950
  | \`mesh_send_task\` | Push a task straight to a specific node/session |
@@ -56962,6 +56997,8 @@ When you compose the task message you dispatch to a node, include this requireme
56962
56997
  | \`mesh_coordinator_prompt_append_set\` | Write/clear that APPEND (append-only; base prompt is not replaceable) |`;
56963
56998
  TOOL_EXPOSURE_PREFLIGHT_SECTION = `## Tool Exposure Preflight
56964
56999
 
57000
+ 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.
57001
+
56965
57002
  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\`.`;
56966
57003
  QUOTA_SECTION = `## Provider Quota
56967
57004
 
@@ -56972,9 +57009,24 @@ Before doing any coordinator work, confirm that the actual callable tool list in
56972
57009
  WORKFLOW_SECTION = `## Orchestration Workflow
56973
57010
 
56974
57011
  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.
56975
- 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.
57012
+ 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 submit the plan as one \`mesh_enqueue_batch\` carrying that \`mission_id\` (a top-level \`mission_id\` applies to every entry). 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.
56976
57013
  3. **Queue / Delegate** \u2014 The Mesh uses an autonomous pull-based Work Queue:
56977
- a. **General Tasks**: Enqueue tasks using \`mesh_enqueue_task\`. Idle node agents will automatically pull tasks from the queue and begin working.
57014
+ a. **Batch-first rule.** Before enqueueing anything, answer ONE question: *when this task reports back, will I read its result and then dispatch more work \u2014 or does the result go to the user with nothing behind it?*
57015
+ - **You will act on the result \u2192 \`mesh_enqueue_batch\`.** If you already intend to read the output and dispatch a next step, that next step is a step you can declare. Declare it: bind the evidence with \`inputs_from\`, branch on it with \`run_if\`, or put your own decision behind a coordinator gate and point the follower at it with \`gated_by\`. "I need to see the result first" is HOW a graph edge is expressed, not a reason to skip the graph \u2014 that is exactly what \`inputs_from\` and gates are for. An investigation you plan to act on is, by this test, a declarable two-step plan.
57016
+ - **The result goes to the user and nothing follows \u2192 \`mesh_enqueue_task\`.** One task, no successor you intend to dispatch yourself. Same-subject continuation of an already-running session is \`mesh_send_task\`, not a new enqueue either way.
57017
+ - Submit the whole materializable plan in ONE batch. A batch may mix worker tasks, \`inputs_from\` bindings, \`run_if\` branches, delayed \`workspace_ref\` preparation, and coordinator gates for approval, Refinery, CI waiting, publish, or deployment. Do not enqueue one known step, wait for it, and then enqueue the next known step by hand.
57018
+ - **A mesh with idle nodes or a short plan does not remove this requirement.** "It is only two steps" and "I will just enqueue the next one when this finishes" are the two ways the graph gets skipped; neither is a reason. Two declarable steps is precisely the case the batch surface exists for.
57019
+ - **"The only thing I am sure of right now is this one task" is not evidence of a single.** That feeling is about your confidence, not about how many steps are settled; two steps are routinely settled at once while it still feels that way. Count the steps you would actually dispatch. If that count is two or more, it is a batch no matter how the plan feels.
57020
+ - **"I cannot choose the next step until I see this result" states that the next step EXISTS.** What the result decides is *which branch you take*, not *whether* there is a follow-up \u2014 and a branch that turns on a predecessor's output is what \`run_if\` selects, what \`inputs_from\` feeds, and what a coordinator gate holds until you decide. Needing to look at evidence before choosing is the ordinary case for a graph edge, not an exception to it.
57021
+ - **A gate or a batch is not overhead when something follows it.** The extra call buys the dispatch of everything behind it, so it costs one call and saves a whole round-trip through you. The only genuinely wasteful gate is one nothing depends on \u2014 a gate no task names in \`gated_by\` opens nothing when released, which the release response tells you about; that case is answered by declaring the follower, not by abandoning the batch.
57022
+
57023
+ When you do use \`mesh_enqueue_task\`, pass \`orchestration_decision\` recording why a single was right (\`single_reason\`: \`only_one_step_known\` / \`future_step_not_specifiable\` / \`same_session_continuation\` / \`legacy_client\` / \`operator_override\`). Omitting it is recorded as \`decision_missing\`.
57024
+
57025
+ Do not invent speculative downstream instructions merely to form a batch \u2014 a step you would not actually dispatch does not belong in the graph. But check the three declaration surfaces before concluding a step is unstatable: \`inputs_from\` when the instruction is stable and only needs predecessor evidence, a coordinator gate when a human/coordinator decision or external side effect must come first, \`run_if\` when the branch turns on a structured result predicate. Only when none of the three can state the future step faithfully is single-task enqueue correct.
57026
+
57027
+ **The line between the two failures.** The rebuttals above are aimed at declining to declare a step you already intend to dispatch; this guard is aimed at fabricating one you do not. They never both apply to the same step: the test is whether the step exists in your plan, not whether its details are settled. A step you intend to run but whose exact branch depends on evidence is KNOWN and belongs in the batch \u2014 that dependency is what the three surfaces express. A step you would have to make up to fill the batch is UNKNOWN and belongs nowhere. When you genuinely cannot tell which one you are looking at, enqueue the single and record \`future_step_not_specifiable\`; that is the honest answer and it is measured.
57028
+
57029
+ 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.
56978
57030
  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.
56979
57031
  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?*
56980
57032
  - **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.
@@ -60495,9 +60547,11 @@ ${rendered}`, "utf-8");
60495
60547
  const materializedNodeIds = advanceReleasedGateDownstream(store, gate, node, nowIso);
60496
60548
  const freshNodes = graphStore.listNodes(gate.graphId);
60497
60549
  const graph = graphStore.getGraph(gate.graphId);
60550
+ let graphCompleted = false;
60498
60551
  if (graph && freshNodes.every(isTerminalEquivalent)) {
60499
60552
  graphStore.updateGraphStatus(gate.graphId, "completed", nowIso, true);
60500
60553
  insertGateOutbox(graphStore, gate.meshId, gate.graphId, "graph_completed", { graphId: gate.graphId }, nowIso);
60554
+ graphCompleted = true;
60501
60555
  } else if (graph?.status === "waiting_gate") {
60502
60556
  const stillWaiting = graphStore.listGatesByGraph(gate.graphId).some((g) => g.gateId !== gate.gateId && (g.state === "awaiting_coordinator" || g.state === "claimed"));
60503
60557
  if (!stillWaiting) graphStore.updateGraphStatus(gate.graphId, "active", nowIso);
@@ -60521,7 +60575,16 @@ ${rendered}`, "utf-8");
60521
60575
  gateId: gate.gateId
60522
60576
  }, nowIso);
60523
60577
  }
60524
- return { released: true, duplicate: false, gate: graphStore.getGate(gate.gateId), materializedNodeIds };
60578
+ return {
60579
+ released: true,
60580
+ duplicate: false,
60581
+ gate: graphStore.getGate(gate.gateId),
60582
+ materializedNodeIds,
60583
+ // Read from the edge set this release already walked — a gate with an empty
60584
+ // directDownstream is terminal, and terminal gates materialize nothing by design.
60585
+ downstreamNodeCount: directDownstream.size,
60586
+ graphCompleted
60587
+ };
60525
60588
  });
60526
60589
  try {
60527
60590
  drainMeshGraphOutbox(input.meshId);
@@ -60782,6 +60845,18 @@ ${rendered}`, "utf-8");
60782
60845
  ...p.replayed ? { replayed: true } : {}
60783
60846
  });
60784
60847
  }
60848
+ function recordSingleEnqueueDecision(meshId, p) {
60849
+ safeAppend(meshId, "single_enqueue_decision", {
60850
+ taskId: p.taskId,
60851
+ enqueueSurface: "single",
60852
+ ...p.missionId ? { missionId: p.missionId } : {},
60853
+ ...p.coordinatorSessionId ? { coordinatorSessionId: p.coordinatorSessionId } : {},
60854
+ orchestrationDecision: p.decision,
60855
+ ...p.decisionMissing ? { decisionMissing: true } : {},
60856
+ ...p.declaredEligibleSingle ? { declaredEligibleSingle: true } : {},
60857
+ ...p.batchCapabilityAvailable ? { batchCapabilityAvailable: p.batchCapabilityAvailable } : {}
60858
+ }, p.taskId);
60859
+ }
60785
60860
  function recordGraphEnqueueValidationFailed(meshId, p) {
60786
60861
  safeAppend(meshId, "graph_enqueue_validation_failed", {
60787
60862
  code: p.code,
@@ -60839,6 +60914,7 @@ ${rendered}`, "utf-8");
60839
60914
  }
60840
60915
  var MESH_VALID_SINGLE_REASONS;
60841
60916
  var MESH_SUPERSEDED_SINGLE_REASONS;
60917
+ var MESH_DECLARED_ELIGIBLE_SINGLE_HINT;
60842
60918
  var init_mesh_graph_provenance = __esm2({
60843
60919
  "src/mesh/mesh-graph-provenance.ts"() {
60844
60920
  "use strict";
@@ -60856,6 +60932,7 @@ ${rendered}`, "utf-8");
60856
60932
  "workspace_unresolved",
60857
60933
  "coordinator_action_between"
60858
60934
  ];
60935
+ MESH_DECLARED_ELIGIBLE_SINGLE_HINT = "You declared known_graph_steps >= 2 but used the single-task surface. Submit the known steps as one mesh_enqueue_batch \u2014 its input bindings, conditions and coordinator gates declare the ones that need predecessor evidence, a condition, or a coordinator action \u2014 instead of enqueueing them one at a time.";
60859
60936
  }
60860
60937
  });
60861
60938
  function turnProjectionActiveWorkStatus(meshId, taskId) {
@@ -61706,8 +61783,28 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61706
61783
  }
61707
61784
  };
61708
61785
  }
61709
- function quotaFactsContextForLiveRouting(mesh, isLocalNode) {
61710
- return { nodes: mesh?.nodes, liveLocalQuota: liveLocalQuotaForRouting(isLocalNode) };
61786
+ function liveLocalProviderEnablementForRouting(loader2, isLocalNode) {
61787
+ if (!loader2) return null;
61788
+ const verdicts = /* @__PURE__ */ new Map();
61789
+ return {
61790
+ isMachineProviderEnabled: (providerType) => loader2.isMachineProviderEnabled(providerType),
61791
+ isMachineQuotaEnabled: (providerType) => loader2.isMachineQuotaEnabled ? loader2.isMachineQuotaEnabled(providerType) : true,
61792
+ isLocalNode(node) {
61793
+ let verdict = verdicts.get(node);
61794
+ if (verdict === void 0) {
61795
+ verdict = !!node && isLocalNode(node) === true;
61796
+ verdicts.set(node, verdict);
61797
+ }
61798
+ return verdict;
61799
+ }
61800
+ };
61801
+ }
61802
+ function quotaFactsContextForLiveRouting(mesh, isLocalNode, providerLoader) {
61803
+ return {
61804
+ nodes: mesh?.nodes,
61805
+ liveLocalQuota: liveLocalQuotaForRouting(isLocalNode),
61806
+ providerEnablement: liveLocalProviderEnablementForRouting(providerLoader, isLocalNode)
61807
+ };
61711
61808
  }
61712
61809
  function directQuotaEntryFor(node, providerType) {
61713
61810
  const facts = node?.nodeFacts;
@@ -61764,6 +61861,28 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61764
61861
  const ageText = Number.isFinite(ageMs2) ? `${Math.round(ageMs2 / 6e4)}m` : "unparseable";
61765
61862
  LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 snapshot age ${ageText} exceeds the ${Math.round(staleAfterMs / 6e4)}m freshness threshold`);
61766
61863
  }
61864
+ function classifyAbsentQuotaReason(node, providerType, context) {
61865
+ const enablement = context?.providerEnablement;
61866
+ if (!enablement) return "unclassified_remote";
61867
+ const sourceNode = cloneSourceNodeFor(node, context);
61868
+ if (!enablement.isLocalNode(node) && !(sourceNode !== void 0 && enablement.isLocalNode(sourceNode))) {
61869
+ return "unclassified_remote";
61870
+ }
61871
+ if (!enablement.isMachineProviderEnabled(providerType)) return "provider_disabled";
61872
+ if (!enablement.isMachineQuotaEnabled(providerType)) return "probe_disabled";
61873
+ return "not_measured";
61874
+ }
61875
+ function logAbsentQuotaFailOpen(node, providerType, policy, now, context) {
61876
+ const staleAfterMs = resolveQuotaRoutingPolicy(policy).staleAfterMs;
61877
+ const nodeId = typeof node?.nodeId === "string" && node.nodeId ? node.nodeId : typeof node?.id === "string" && node.id ? node.id : "unknown";
61878
+ const key2 = `${nodeId} ${providerType}`;
61879
+ const last = absentFailOpenLoggedAt.get(key2);
61880
+ if (last !== void 0 && now - last < staleAfterMs) return;
61881
+ absentFailOpenLoggedAt.set(key2, now);
61882
+ const reason = classifyAbsentQuotaReason(node, providerType, context);
61883
+ const reasonText = reason === "probe_disabled" ? "quota probing is disabled for this provider on this machine (quotaEnabled: false \u2014 an intended opt-out, not a fault)" : reason === "provider_disabled" ? "this provider is not enabled on this machine" : reason === "not_measured" ? "no snapshot has been measured yet" : "this daemon cannot read that node's machineProviders config (remote node, or its own daemon has not injected one) \u2014 cannot tell not-yet-measured from an opt-out; check the owning node's config directly";
61884
+ LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 no quota entry (${reason}: ${reasonText})`);
61885
+ }
61767
61886
  function isSessionResetImminent(session, facts, quota, imminentMs, now) {
61768
61887
  const resetsAt = Number(session?.resetsAt);
61769
61888
  if (!Number.isFinite(resetsAt) || resetsAt <= 0) return false;
@@ -61785,7 +61904,10 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61785
61904
  }
61786
61905
  function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context) {
61787
61906
  const entry = quotaEntryFor(node, providerType, context, now);
61788
- if (!entry) return null;
61907
+ if (!entry) {
61908
+ logAbsentQuotaFailOpen(node, providerType, policy, now, context);
61909
+ return null;
61910
+ }
61789
61911
  const { facts, quota } = entry;
61790
61912
  let sessionTrustworthy = true;
61791
61913
  let weeklyTrustworthy = true;
@@ -61947,6 +62069,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61947
62069
  var PROVIDER_QUOTA_EXHAUSTED_SKIP_REASON;
61948
62070
  var ALL_PROVIDERS_QUOTA_GATED_SKIP_REASON;
61949
62071
  var staleFailOpenLoggedAt;
62072
+ var absentFailOpenLoggedAt;
61950
62073
  var DEFAULT_WEEKLY_WINDOW_MINUTES;
61951
62074
  var DEFAULT_SESSION_WINDOW_MINUTES;
61952
62075
  var lastQuotaRankingByNode;
@@ -61962,6 +62085,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
61962
62085
  PROVIDER_QUOTA_EXHAUSTED_SKIP_REASON = "provider_quota_exhausted";
61963
62086
  ALL_PROVIDERS_QUOTA_GATED_SKIP_REASON = "all_providers_quota_gated";
61964
62087
  staleFailOpenLoggedAt = /* @__PURE__ */ new Map();
62088
+ absentFailOpenLoggedAt = /* @__PURE__ */ new Map();
61965
62089
  DEFAULT_WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
61966
62090
  DEFAULT_SESSION_WINDOW_MINUTES = 5 * 60;
61967
62091
  lastQuotaRankingByNode = /* @__PURE__ */ new Map();
@@ -66498,7 +66622,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
66498
66622
  LOG2.info("MeshQueue", `Deferring queue claim for node ${nodeId} (${sessionId}): an auto fast-forward is mutating its workspace \u2014 task left pending, claim re-fires next tick`);
66499
66623
  return false;
66500
66624
  }
66501
- const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode));
66625
+ const quotaClaimBlock = evaluateProviderQuotaGate(node, providerType, mesh?.policy?.quotaRouting ?? null, Date.now(), quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader));
66502
66626
  if (quotaClaimTrace) quotaClaimTrace.evaluated += 1;
66503
66627
  if (quotaClaimBlock) {
66504
66628
  const observation = { nodeId, sessionId, providerType, block: quotaClaimBlock };
@@ -67236,7 +67360,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67236
67360
  // pass it through for the 'fitness' strategy's task→slot ranking. The
67237
67361
  // mesh's quotaRouting thresholds ride along so the fitness score can
67238
67362
  // include the quota-headroom spread bonus (fail-open when unset).
67239
- { bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode) }
67363
+ { bumpCursor: true, task: { difficulty: task.difficulty, requiredTags: task.requiredTags }, quotaRouting: mesh?.policy?.quotaRouting ?? null, quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader) }
67240
67364
  ).map((c) => c.node);
67241
67365
  const skippedCandidates = [];
67242
67366
  const SKIPPED_CANDIDATES_MAX = 5;
@@ -67295,7 +67419,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67295
67419
  }
67296
67420
  autoLaunchInProgress.add(launchKey);
67297
67421
  try {
67298
- const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode), task.id);
67422
+ const resolved = await resolveUsableProvider(components, nodeId, node, meshId, task.requiredTags, { difficulty: task.difficulty, requiredTags: task.requiredTags }, mesh?.policy?.quotaRouting ?? null, quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader), task.id);
67299
67423
  if (!resolved.providerType) {
67300
67424
  markSkip(nodeId, resolved.reason || "provider_unusable");
67301
67425
  continue;
@@ -67436,7 +67560,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
67436
67560
  task: { difficulty: task.difficulty, requiredTags: task.requiredTags },
67437
67561
  resolved,
67438
67562
  quotaRouting: mesh?.policy?.quotaRouting ?? null,
67439
- quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode),
67563
+ quotaFactsContext: quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader),
67440
67564
  skippedCandidates,
67441
67565
  requiredTagsResult: {
67442
67566
  required: requiredTags,
@@ -71489,7 +71613,7 @@ ${cleanBody}`;
71489
71613
  // daemon's live quota cache, not the git_status-stamped
71490
71614
  // nodeFacts copy (mesh-quota-routing). Remote nodes keep
71491
71615
  // reading their reported nodeFacts.
71492
- quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode)
71616
+ quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, components.providerLoader)
71493
71617
  ) : { action: "block" };
71494
71618
  const relaunchProviderType = relaunch.action === "block" ? null : relaunch.providerType;
71495
71619
  if (node) {
@@ -82395,6 +82519,7 @@ ${e?.stderr || ""}`;
82395
82519
  MAX_SIZE_ROTATION_GENERATIONS: () => MAX_SIZE_ROTATION_GENERATIONS,
82396
82520
  MESH_CONVERGE_FAST_FORWARD_TAG: () => MESH_CONVERGE_FAST_FORWARD_TAG,
82397
82521
  MESH_CONVERGE_REFINE_TAG: () => MESH_CONVERGE_REFINE_TAG,
82522
+ MESH_DECLARED_ELIGIBLE_SINGLE_HINT: () => MESH_DECLARED_ELIGIBLE_SINGLE_HINT,
82398
82523
  MESH_GATE_DEFAULT_LEASE_SECONDS: () => MESH_GATE_DEFAULT_LEASE_SECONDS,
82399
82524
  MESH_GATE_NAMED_OUTCOMES: () => MESH_GATE_NAMED_OUTCOMES,
82400
82525
  MESH_GATE_RELEASE_PATCH_KEYS: () => MESH_GATE_RELEASE_PATCH_KEYS,
@@ -82793,6 +82918,7 @@ ${e?.stderr || ""}`;
82793
82918
  recordMeshToolCall: () => recordMeshToolCall,
82794
82919
  recordMissingSessionAttempt: () => recordMissingSessionAttempt2,
82795
82920
  recordSessionUsage: () => recordSessionUsage,
82921
+ recordSingleEnqueueDecision: () => recordSingleEnqueueDecision,
82796
82922
  recoverExpiredWorkspaceSagas: () => recoverExpiredWorkspaceSagas,
82797
82923
  registerExtensionProviders: () => registerExtensionProviders,
82798
82924
  registerMeshCoordinator: () => registerMeshCoordinator,
@@ -85682,20 +85808,29 @@ ${e?.stderr || ""}`;
85682
85808
  if (!fromNodeId) continue;
85683
85809
  addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
85684
85810
  }
85811
+ const gateRefsForNode = new Set((t.gated_by ?? []).map((r) => r.trim()).filter(Boolean));
85685
85812
  for (const sourceRef of collectRunIfSourceRefs(t.run_if)) {
85686
85813
  const fromNodeId = nodeIdByRef.get(sourceRef);
85687
85814
  if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
85815
+ if (gateNodeIdByRef.has(sourceRef)) {
85816
+ gateRefsForNode.add(sourceRef);
85817
+ continue;
85818
+ }
85688
85819
  if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i])) continue;
85689
85820
  addEdge(fromNodeId, nodeIdByIndex[i], "conditional", omitOnSkip);
85690
85821
  }
85691
85822
  for (const sourceRef of collectInputSourceRefs(t.inputs_from)) {
85692
85823
  const fromNodeId = nodeIdByRef.get(sourceRef);
85693
85824
  if (!fromNodeId || fromNodeId === nodeIdByIndex[i]) continue;
85825
+ if (gateNodeIdByRef.has(sourceRef)) {
85826
+ gateRefsForNode.add(sourceRef);
85827
+ continue;
85828
+ }
85694
85829
  if (edges.some((e) => e.fromNodeId === fromNodeId && e.toNodeId === nodeIdByIndex[i] && e.kind === "requires")) continue;
85695
85830
  addEdge(fromNodeId, nodeIdByIndex[i], "requires", omitOnSkip);
85696
85831
  }
85697
- for (const gateRef of t.gated_by ?? []) {
85698
- const fromNodeId = gateNodeIdByRef.get(gateRef.trim());
85832
+ for (const gateRef of gateRefsForNode) {
85833
+ const fromNodeId = gateNodeIdByRef.get(gateRef);
85699
85834
  if (!fromNodeId) continue;
85700
85835
  addEdge(fromNodeId, nodeIdByIndex[i], "gate", false);
85701
85836
  }