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