@adhdev/daemon-core 0.9.82-rc.424 → 0.9.82-rc.425

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.424",
3
+ "version": "0.9.82-rc.425",
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.424",
49
+ "@adhdev/mesh-shared": "0.9.82-rc.425",
50
50
  "@adhdev/session-host-core": "*",
51
51
  "@agentclientprotocol/sdk": "^0.16.1",
52
52
  "ajv": "^8.20.0",
@@ -44,7 +44,7 @@ import type { DaemonComponents } from '../boot/daemon-lifecycle.js';
44
44
  import type { LocalMeshEntry } from '../repo-mesh-types.js';
45
45
  import { loadConfig } from '../config/config.js';
46
46
  import { listMeshes } from '../config/mesh-config.js';
47
- import { LOG } from '../logging/logger.js';
47
+ import { LOG, getLogLevel } from '../logging/logger.js';
48
48
  import { drainPendingMeshCoordinatorEvents, getPendingMeshCoordinatorEvents, buildPendingEventFingerprint, queuePendingMeshCoordinatorEvent } from './mesh-events-pending.js';
49
49
  import type { PendingMeshCoordinatorEvent } from './mesh-events-pending.js';
50
50
  import { appendLedgerEntry } from './mesh-ledger.js';
@@ -319,6 +319,38 @@ function findLiveCoordinators(components: DaemonComponents): LiveCoordinator[] {
319
319
  ? (inst as any).isModalParked() === true
320
320
  : (status === 'waiting_choice' || status === 'waiting_approval');
321
321
  const sessionId = readNonEmptyString(state.instanceId);
322
+ // ── NOTIF (B) desync diagnostic (read-only, no behavior change) ───────────
323
+ // The confirmed (B) defect: a coordinator whose FSM is idle (status above ===
324
+ // 'idle' for minutes) is nonetheless classified busy here, so the generating/
325
+ // modal-park hold never drains and a worker completion is stranded until the
326
+ // user's next turn edge. Static analysis found no code path where getState()
327
+ // returns generating while lastStatus and the adapter raw are both idle — so the
328
+ // divergence is a runtime desync between the three status sources. Capture all
329
+ // three (plus the auto-approve mask state that getState() overlays at :803) for
330
+ // EVERY mesh-coordinator candidate on this tick, so the source that diverges from
331
+ // the others can be read directly against the same-tick "skip → generating"/
332
+ // "skip → modal-parked" hold logs below (pair by sessionId + timestamp).
333
+ //
334
+ // CRITICAL: reuse the `state` already fetched above (line ~301) — do NOT call
335
+ // getState() again. getState() runs maybeAutoApproveStatus() as a side effect,
336
+ // which would mutate the very auto-approve mask we are trying to observe. The
337
+ // adapter raw read uses allowParse:false, which only reads engine.activeModal and
338
+ // is side-effect-free.
339
+ if (getLogLevel() === 'debug') {
340
+ let adapterRaw = '?';
341
+ try {
342
+ const a = (inst as any).adapter;
343
+ if (a && typeof a.getStatus === 'function') {
344
+ adapterRaw = readNonEmptyString(a.getStatus({ allowParse: false })?.status) || '?';
345
+ }
346
+ } catch (e: any) {
347
+ adapterRaw = `err:${e?.message || e}`;
348
+ }
349
+ const lastStatus = readNonEmptyString((inst as any).lastStatus) || '?';
350
+ const autoApproveBusy = (inst as any).autoApproveBusy;
351
+ const maskSince = (inst as any).autoApproveMaskSince;
352
+ LOG.debug('MeshReconcile', `coordDiag sess=${sessionId || '?'} mesh=${meshId} getState=${status || '?'} lastStatus=${lastStatus} adapterRaw=${adapterRaw} autoApproveBusy=${autoApproveBusy === true} maskSince=${maskSince || 0}`);
353
+ }
322
354
  // Modal-park transition observability: a coordinator entering modal-park is what
323
355
  // begins holding completion events under `modal_parked`; one leaving it is what
324
356
  // drains them. Both transitions were previously SILENT (the operator had no log
@@ -902,6 +934,12 @@ export async function runMeshReconcileTick(components: DaemonComponents): Promis
902
934
  }
903
935
  }
904
936
  LOG.info('MeshReconcile', `Reconcile skip → modal-parked: holding pending event(s) for mesh ${meshId} (${modalParkedCoordinators.length} coordinator(s) awaiting a modal answer; events left queued${orphanEscaped > 0 ? `; ${orphanEscaped} orphan-targeted event(s) routed to strict-route TTL` : ''})`);
937
+ // NOTIF (B) diagnostic: name the session(s) classified modal-parked so the
938
+ // same-tick coordDiag line (paired by sessionId) shows whether the modal-park
939
+ // overlay is a real human-await or an unreleased mask (the getState_overlay origin).
940
+ if (getLogLevel() === 'debug') {
941
+ LOG.debug('MeshReconcile', `coordHoldModalParked mesh=${meshId} heldFor=[${modalParkedCoordinators.map(c => c.sessionId || '?').join(',')}] (these were classified modal-parked; cross-ref same-tick coordDiag by sessionId)`);
942
+ }
905
943
  // C1: mirror held terminal events into the ledger so a held completion's
906
944
  // worker summary is auditable/recoverable even if the modal is never
907
945
  // resolved, the coordinator restarts, or the pending file is later trimmed.
@@ -937,6 +975,14 @@ export async function runMeshReconcileTick(components: DaemonComponents): Promis
937
975
  }
938
976
  if (hasPending) {
939
977
  LOG.info('MeshReconcile', `Reconcile skip → generating: holding pending event(s) for mesh ${meshId} (${generatingCoordinators.length} coordinator(s) busy; events left queued for the next idle tick)`);
978
+ // NOTIF (B) diagnostic: this is the hold that strands the completion. Name
979
+ // the sessionId(s) the loop just classified non-idle/non-modal so the
980
+ // same-tick coordDiag line above (paired by sessionId) reveals which status
981
+ // source diverged. If a coordDiag for one of these sessions shows getState
982
+ // (or lastStatus/adapterRaw) === idle, that is the runtime desync origin.
983
+ if (getLogLevel() === 'debug') {
984
+ LOG.debug('MeshReconcile', `coordHoldGenerating mesh=${meshId} heldFor=[${generatingCoordinators.map(c => c.sessionId || '?').join(',')}] (these were classified busy; cross-ref same-tick coordDiag by sessionId)`);
985
+ }
940
986
  recordHeldTerminalEventsToLedger(
941
987
  meshId,
942
988
  drainDaemonIds.length > 0 ? drainDaemonIds : (localDaemonId ? [localDaemonId] : []),