@adhdev/daemon-core 0.9.82-rc.63 → 0.9.82-rc.65

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.63",
3
+ "version": "0.9.82-rc.65",
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",
@@ -166,6 +166,7 @@ function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
166
166
  meshId: readStringValue(status?.meshId, status?.mesh_id) ?? null,
167
167
  refreshedAt: readStringValue(status?.refreshedAt, status?.refreshed_at) ?? null,
168
168
  sourceOfTruth: status?.sourceOfTruth ?? null,
169
+ branchConvergenceSummary: status?.branchConvergenceSummary ?? status?.branch_convergence_summary ?? null,
169
170
  nodeCount: nodes.length,
170
171
  nodes: nodes.map((node: any) => ({
171
172
  nodeId: readStringValue(node?.nodeId, node?.id) ?? null,
@@ -182,6 +183,7 @@ function summarizeRepoMeshStatusDebug(status: any): Record<string, unknown> {
182
183
  gitProbePending: node?.gitProbePending === true,
183
184
  launchReady: node?.launchReady === true,
184
185
  git: summarizeRepoMeshDebugGit(node?.git),
186
+ branchConvergence: node?.branchConvergence ?? node?.branch_convergence ?? null,
185
187
  })),
186
188
  };
187
189
  }
@@ -249,6 +251,10 @@ function normalizeInlineMeshGitStatus(
249
251
  headCommit: readStringValue(status.headCommit) ?? null,
250
252
  headMessage: readStringValue(status.headMessage) ?? null,
251
253
  upstream: readStringValue(status.upstream) ?? null,
254
+ upstreamStatus: readStringValue(status.upstreamStatus, status.upstream_status)
255
+ ?? (readStringValue(status.upstream) ? 'unchecked' : 'no_upstream'),
256
+ upstreamFetchedAt: readNumberValue(status.upstreamFetchedAt, status.upstream_fetched_at),
257
+ upstreamFetchError: readStringValue(status.upstreamFetchError, status.upstream_fetch_error),
252
258
  ahead: readNumberValue(status.ahead) ?? 0,
253
259
  behind: readNumberValue(status.behind) ?? 0,
254
260
  staged: readNumberValue(status.staged) ?? 0,
@@ -490,8 +496,16 @@ function reconcileInlineMeshCache(cached: any, incoming: any): any {
490
496
  }
491
497
 
492
498
  function hasGitWorktreeChanges(git: Record<string, unknown> | null | undefined): boolean {
493
- if (!git) return false;
494
- return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
499
+ return countGitWorktreeChanges(git) > 0;
500
+ }
501
+
502
+ function countGitWorktreeChanges(git: Record<string, unknown> | null | undefined): number {
503
+ if (!git) return 0;
504
+ return Number(git.staged || 0)
505
+ + Number(git.modified || 0)
506
+ + Number(git.untracked || 0)
507
+ + Number(git.deleted || 0)
508
+ + Number(git.renamed || 0);
495
509
  }
496
510
 
497
511
  function getGitSubmoduleDriftState(git: Record<string, unknown> | null | undefined): { dirty: boolean; outOfSync: boolean } {
@@ -516,6 +530,167 @@ function deriveMeshNodeHealthFromGit(git: Record<string, unknown> | null | undef
516
530
  return 'online';
517
531
  }
518
532
 
533
+ function readMeshNodeLabel(status: Record<string, unknown>, node: any): string {
534
+ return readStringValue(status.nodeId, node?.id, node?.nodeId) ?? 'unknown';
535
+ }
536
+
537
+ function buildInlineMeshBranchConvergence(args: {
538
+ mesh: any;
539
+ node: any;
540
+ status: Record<string, unknown>;
541
+ }): Record<string, unknown> {
542
+ const git = readObjectRecord(args.status.git);
543
+ const nodeLabel = readMeshNodeLabel(args.status, args.node);
544
+ const defaultBranch = readStringValue(args.mesh?.defaultBranch) ?? 'main';
545
+ const branch = readStringValue(git.branch, args.node?.worktreeBranch) ?? null;
546
+ const upstream = readStringValue(git.upstream) ?? null;
547
+ const upstreamStatus = readStringValue(git.upstreamStatus, git.upstream_status)
548
+ ?? (upstream ? 'unchecked' : 'no_upstream');
549
+ const ahead = readNumberValue(git.ahead) ?? 0;
550
+ const behind = readNumberValue(git.behind) ?? 0;
551
+ const uncommittedChanges = countGitWorktreeChanges(git);
552
+ const hasConflicts = readBooleanValue(git.hasConflicts)
553
+ ?? (Array.isArray(git.conflictFiles) && git.conflictFiles.length > 0);
554
+ const base = {
555
+ defaultBranch,
556
+ branch,
557
+ upstream,
558
+ upstreamStatus,
559
+ ahead,
560
+ behind,
561
+ isWorktree: args.node?.isLocalWorktree === true || args.status.isLocalWorktree === true,
562
+ isDefaultBranch: branch === defaultBranch,
563
+ };
564
+
565
+ if (readBooleanValue(git.isGitRepo) !== true) {
566
+ return {
567
+ ...base,
568
+ status: 'blocked_review',
569
+ needsConvergence: true,
570
+ reason: 'git_status_unavailable',
571
+ nextStep: `Resolve git status for node '${nodeLabel}' before marking the task complete.`,
572
+ };
573
+ }
574
+
575
+ if (!branch) {
576
+ return {
577
+ ...base,
578
+ status: 'blocked_review',
579
+ needsConvergence: true,
580
+ reason: 'branch_unknown',
581
+ nextStep: `Inspect node '${nodeLabel}' git branch before deciding whether it is merged to ${defaultBranch}.`,
582
+ };
583
+ }
584
+
585
+ if (hasConflicts || uncommittedChanges > 0) {
586
+ return {
587
+ ...base,
588
+ status: 'not_mergeable',
589
+ needsConvergence: true,
590
+ reason: hasConflicts ? 'conflicts_present' : 'dirty_workspace',
591
+ nextStep: `Commit, checkpoint, or resolve node '${nodeLabel}' before any main convergence step.`,
592
+ };
593
+ }
594
+
595
+ if (branch === defaultBranch) {
596
+ if (upstream && upstreamStatus !== 'fresh') {
597
+ return {
598
+ ...base,
599
+ status: 'blocked_review',
600
+ needsConvergence: true,
601
+ reason: 'default_branch_upstream_unverified',
602
+ nextStep: `Refresh ${defaultBranch}'s upstream refs or resolve the fetch failure before declaring convergence complete for node '${nodeLabel}'.`,
603
+ };
604
+ }
605
+ if (ahead > 0 || behind > 0) {
606
+ return {
607
+ ...base,
608
+ status: 'blocked_review',
609
+ needsConvergence: true,
610
+ reason: 'default_branch_not_even_with_upstream',
611
+ nextStep: `Bring ${defaultBranch} even with its upstream before declaring convergence complete.`,
612
+ };
613
+ }
614
+ return {
615
+ ...base,
616
+ status: 'merged_to_main',
617
+ needsConvergence: false,
618
+ reason: 'clean_default_branch',
619
+ nextStep: null,
620
+ };
621
+ }
622
+
623
+ if (args.node?.isLocalWorktree === true || args.status.isLocalWorktree === true) {
624
+ return {
625
+ ...base,
626
+ status: 'cleanup_candidate',
627
+ needsConvergence: true,
628
+ reason: 'clean_non_default_worktree_branch',
629
+ nextStep: `Run mesh_refine_node(node_id: "${nodeLabel}") or explicitly classify this worktree as blocked_review/not_mergeable before ending the task.`,
630
+ };
631
+ }
632
+
633
+ if (upstream && upstreamStatus !== 'fresh') {
634
+ return {
635
+ ...base,
636
+ status: 'blocked_review',
637
+ needsConvergence: true,
638
+ reason: 'feature_branch_upstream_unverified',
639
+ nextStep: `Refresh branch '${branch}' upstream refs or resolve the fetch failure before deciding whether it is ready to merge into ${defaultBranch}.`,
640
+ };
641
+ }
642
+
643
+ if (!upstream || ahead > 0 || behind > 0) {
644
+ return {
645
+ ...base,
646
+ status: 'blocked_review',
647
+ needsConvergence: true,
648
+ reason: !upstream ? 'feature_branch_missing_upstream' : 'feature_branch_not_even_with_upstream',
649
+ nextStep: `Push or reconcile branch '${branch}', then merge it into ${defaultBranch} or mark it not_mergeable with a reason.`,
650
+ };
651
+ }
652
+
653
+ return {
654
+ ...base,
655
+ status: 'pushed_feature_branch_needs_merge',
656
+ needsConvergence: true,
657
+ reason: 'clean_non_default_branch',
658
+ nextStep: `Review and merge branch '${branch}' into ${defaultBranch}; do not report the task as fully complete while it remains off main.`,
659
+ };
660
+ }
661
+
662
+ function applyInlineMeshBranchConvergence(mesh: any, node: any, status: Record<string, unknown>): void {
663
+ const git = readObjectRecord(status.git);
664
+ if (Object.keys(git).length === 0 && !status.gitProbePending) return;
665
+ const uncommittedChanges = countGitWorktreeChanges(git);
666
+ status.isDirty = uncommittedChanges > 0;
667
+ status.uncommittedChanges = uncommittedChanges;
668
+ status.branchConvergence = buildInlineMeshBranchConvergence({ mesh, node, status });
669
+ }
670
+
671
+ function summarizeInlineMeshBranchConvergence(nodes: Array<Record<string, unknown>>): Record<string, unknown> {
672
+ const followUps = nodes
673
+ .filter(node => readObjectRecord(node.branchConvergence).needsConvergence === true)
674
+ .map(node => {
675
+ const convergence = readObjectRecord(node.branchConvergence);
676
+ return {
677
+ nodeId: node.nodeId,
678
+ workspace: node.workspace,
679
+ branch: convergence.branch,
680
+ status: convergence.status,
681
+ reason: convergence.reason,
682
+ nextStep: convergence.nextStep,
683
+ };
684
+ });
685
+
686
+ return {
687
+ needsFollowUp: followUps.length > 0,
688
+ unresolvedCount: followUps.length,
689
+ requiredFinalStates: ['merged_to_main', 'pushed_feature_branch_needs_merge', 'blocked_review', 'cleanup_candidate', 'not_mergeable'],
690
+ followUps,
691
+ };
692
+ }
693
+
519
694
  function readCachedInlineMeshActiveSessions(node: any): string[] {
520
695
  const cachedStatus = readObjectRecord(node?.cachedStatus);
521
696
  const activeSession = readObjectRecord(cachedStatus.activeSession);
@@ -620,7 +795,7 @@ async function probeRemoteMeshGitStatus(args: {
620
795
  }): Promise<Record<string, unknown> | null> {
621
796
  if (!args.dispatchMeshCommand) return null;
622
797
  const remoteResult = await Promise.race([
623
- args.dispatchMeshCommand(args.daemonId, 'git_status', { workspace: args.workspace }),
798
+ args.dispatchMeshCommand(args.daemonId, 'git_status', { workspace: args.workspace, refreshUpstream: true }),
624
799
  new Promise<never>((_, reject) => setTimeout(() => reject(new Error('timeout')), args.timeoutMs)),
625
800
  ]) as any;
626
801
  const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
@@ -1466,6 +1641,7 @@ export class DaemonCommandRouter {
1466
1641
  const nextStatus = { ...statusNode };
1467
1642
  nextStatus.git = liveGit;
1468
1643
  nextStatus.health = deriveMeshNodeHealthFromGit(liveGit);
1644
+ applyInlineMeshBranchConvergence(mesh, inlineNode, nextStatus);
1469
1645
  nextStatus.launchReady = readBooleanValue(nextStatus.launchReady) ?? true;
1470
1646
  const connection = readObjectRecord(nextStatus.connection);
1471
1647
  const connectionState = readStringValue(connection.state);
@@ -1505,6 +1681,7 @@ export class DaemonCommandRouter {
1505
1681
  error: 'Selected coordinator could not confirm direct mesh truth for every remote node yet.',
1506
1682
  } : {}),
1507
1683
  sourceOfTruth: nextSourceOfTruth,
1684
+ branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodes),
1508
1685
  nodes,
1509
1686
  };
1510
1687
  }
@@ -4639,11 +4816,13 @@ export class DaemonCommandRouter {
4639
4816
  node,
4640
4817
  pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : undefined,
4641
4818
  )) {
4819
+ applyInlineMeshBranchConvergence(mesh, node, status);
4642
4820
  finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4643
4821
  nodeStatuses.push(status);
4644
4822
  continue;
4645
4823
  }
4646
4824
  if (meshRecord?.source === 'inline_cache' && !isSelfNode) {
4825
+ applyInlineMeshBranchConvergence(mesh, node, status);
4647
4826
  finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4648
4827
  nodeStatuses.push(status);
4649
4828
  continue;
@@ -4669,6 +4848,7 @@ export class DaemonCommandRouter {
4669
4848
  } else {
4670
4849
  applyCachedInlineMeshNodeStatus(status, node);
4671
4850
  }
4851
+ applyInlineMeshBranchConvergence(mesh, node, status);
4672
4852
  finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
4673
4853
  nodeStatuses.push(status);
4674
4854
  }
@@ -4709,6 +4889,7 @@ export class DaemonCommandRouter {
4709
4889
  } : {}),
4710
4890
  historicalEvidenceOnly: ['recoveryHints', 'ledger.summary', 'queue.summary'],
4711
4891
  },
4892
+ branchConvergenceSummary: summarizeInlineMeshBranchConvergence(nodeStatuses),
4712
4893
  nodes: nodeStatuses,
4713
4894
  queue: { tasks: queue, summary: queueSummary },
4714
4895
  ledger: { entries: ledgerEntries, summary: ledgerSummary },