agent-relay-orchestrator 0.21.0 → 0.23.0

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": "agent-relay-orchestrator",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "test": "bun test"
17
17
  },
18
18
  "dependencies": {
19
- "agent-relay-sdk": "0.2.12"
19
+ "agent-relay-sdk": "0.2.13"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/bun": "latest",
@@ -789,7 +789,9 @@ export function previewWorkspaceMerge(input: { worktreePath?: string; baseRef?:
789
789
  : gitState.landed
790
790
  ? "already merged into base (squash/cherry-pick)"
791
791
  : "no commits to merge";
792
- return { ...base, reason };
792
+ // Nothing to land and the worktree is clean — a no-op the land path resolves to
793
+ // a terminal state rather than parking forever in the steward queue (#230).
794
+ return { ...base, reason, noop: true };
793
795
  }
794
796
  if (gitState.baseRef && input.worktreePath) {
795
797
  const conflict = predictConflict(resolve(input.worktreePath), gitState.baseRef);
@@ -820,6 +822,10 @@ export function mergeWorkspace(input: WorkspaceMergeInput): WorkspaceMergeResult
820
822
 
821
823
  if (preview.missing) return head({ status: "cleaned", error: preview.reason });
822
824
  if (preview.error) return head({ status: "review_requested", error: preview.error });
825
+ // Nothing to land (ahead=0, clean): the branch tree is already in base. Resolve it
826
+ // to a terminal state so it leaves the steward queue instead of looping forever in
827
+ // review_requested (#230). Reclaim the spent worktree/branch when the owner is gone.
828
+ if (preview.noop) return resolveNoopMerge(input, worktreePath, repoRoot, branch, head);
823
829
  if (preview.reason) return head({ status: "review_requested", error: preview.reason });
824
830
  if (preview.conflict) return head({ conflict: true, status: "conflict", error: "merge would conflict with base" });
825
831
 
@@ -827,6 +833,31 @@ export function mergeWorkspace(input: WorkspaceMergeInput): WorkspaceMergeResult
827
833
  return mergeRebaseFf(input, worktreePath, repoRoot, branch, preview, head);
828
834
  }
829
835
 
836
+ /**
837
+ * Resolve a no-op land (#230): ahead=0 with a clean worktree, so the branch's tree
838
+ * is already contained in base (never diverged, or already landed via
839
+ * squash/cherry-pick/PR). There's nothing to merge — mark the workspace terminal
840
+ * (`merged`, `noop: true`) so it leaves the steward queue. When the owner is gone
841
+ * (`deleteBranch !== false`) the spent worktree/branch are reclaimed too, mirroring a
842
+ * terminal land; a live owner keeps its worktree (cleanup reclaims it later). Safe:
843
+ * there is no unmerged work to lose.
844
+ */
845
+ function resolveNoopMerge(
846
+ input: WorkspaceMergeInput,
847
+ worktreePath: string,
848
+ repoRoot: string,
849
+ branch: string | undefined,
850
+ head: (field: Partial<WorkspaceMergeResult>) => WorkspaceMergeResult,
851
+ ): WorkspaceMergeResult {
852
+ if (input.deleteBranch !== false && branch) {
853
+ const removed = git(["worktree", "remove", "--force", worktreePath], repoRoot);
854
+ const worktreeRemoved = removed.ok;
855
+ const branchDeleted = worktreeRemoved ? git(["branch", "-D", branch], repoRoot).ok : false;
856
+ return head({ status: "merged", noop: true, worktreeRemoved, branchDeleted, error: undefined });
857
+ }
858
+ return head({ status: "merged", noop: true, error: undefined });
859
+ }
860
+
830
861
  function mergePr(
831
862
  input: WorkspaceMergeInput,
832
863
  worktreePath: string,