@adhdev/daemon-core 0.9.82-rc.393 → 0.9.82-rc.395

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/daemon-core",
3
- "version": "0.9.82-rc.393",
3
+ "version": "0.9.82-rc.395",
4
4
  "description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,7 +46,7 @@
46
46
  "author": "vilmire",
47
47
  "license": "AGPL-3.0-or-later",
48
48
  "dependencies": {
49
- "@adhdev/mesh-shared": "0.9.82-rc.393",
49
+ "@adhdev/mesh-shared": "0.9.82-rc.395",
50
50
  "@adhdev/session-host-core": "*",
51
51
  "@agentclientprotocol/sdk": "^0.16.1",
52
52
  "ajv": "^8.20.0",
@@ -269,6 +269,37 @@ export function tryAssignQueueTask(
269
269
  const mesh = getMeshWithCache(components, meshId);
270
270
  const node = mesh?.nodes.find((n: any) => readMeshNodeId(n) === nodeId);
271
271
 
272
+ // WORKTREE-CLAIM-GATE-BYPASS: the SINGLE claim-time gate for the worktree-bootstrap defer.
273
+ // tryAssignQueueTask is the one funnel every claim path flows through — the event-driven
274
+ // agent:ready drain, the triggerMeshQueue idle-session drain (local + remote), the
275
+ // auto-launch claim, and the PHASE 3 reconcile re-drain all call it. The agent:ready handler
276
+ // (mesh-event-forwarding) deferred its OWN claim while a worktree node's bootstrap was still
277
+ // 'running', but it ALSO called setRemoteIdleSession first — registering the session as a
278
+ // claim candidate. A concurrent triggerMeshQueue drain then pulled that candidate and claimed
279
+ // through tryAssignQueueTask within ~0.16s, BYPASSING the event-handler-local defer: the task
280
+ // dispatched into a half-built worktree (native addons not yet installed → child daemon dies
281
+ // → empty session, totalMessages=0). The transport ack returns ok:true, so neither the
282
+ // assigned-stranded watchdog nor the pending-only PHASE 3 reconcile ever re-fires it → the
283
+ // session is stranded empty forever.
284
+ //
285
+ // Lowering the gate HERE makes the defer a property of the claim itself, not of one caller:
286
+ // a worktree node whose bootstrap is still 'running' can never be claimed from any path. The
287
+ // task stays pending (we return false WITHOUT touching its status — no fail/cancel), so the
288
+ // bootstrap_complete refire (triggerMeshQueue re-fired on worktree_bootstrap_complete) re-runs
289
+ // this claim and passes once status is no longer 'running'. The registered remote-idle session
290
+ // persists for REMOTE_IDLE_SESSION_TTL_MS (5min > observed ~2m8s bootstrap), so the refire
291
+ // still finds a live candidate to re-claim. Identity uses meshNodeIdMatches (the same shared
292
+ // 3-form normalizer the defer guard uses), never a raw === — canon-identity regression guard.
293
+ // Conservative: any non-'running' status (idle/complete/failed/absent/unknown) does NOT gate,
294
+ // so a base node and a fully-bootstrapped worktree keep prior behavior exactly.
295
+ const gateNode = mesh?.nodes?.find((n: any) => meshNodeIdMatches(n, nodeId)) as
296
+ | { worktreeBootstrap?: { status?: string } }
297
+ | undefined;
298
+ if (gateNode?.worktreeBootstrap?.status === 'running') {
299
+ LOG.info('MeshQueue', `Gating queue claim for worktree node ${nodeId} (${sessionId}): worktree bootstrap still running — task left pending; claim re-fires once bootstrap reaches a terminal state (guards against dispatching into a half-built worktree → empty session)`);
300
+ return false;
301
+ }
302
+
272
303
  // WTCLAIM (fix-B extended to the enqueue→claim path): a base-targeted task must never be
273
304
  // claimed by — and dispatched into — a co-located worktree-clone session, nor vice versa.
274
305
  // The drain candidate's nodeId is derived from settings.meshNodeId || settings.nodeId