Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 92 additions & 26 deletions orchestrator/gateway_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2094,15 +2094,12 @@ def merge_base(
ambient slice/pipeline pass their token through ``bearer_token``
to avoid a redundant register/delete round-trip.

Used by slice-4 TASK-4-3's ``_resolve_slice_base_branch``
merge-base fallback: legacy slices whose
``parent_branch_at_creation`` is empty AND whose integration
branch still exists on origin compute their fork SHA against
``origin/main`` to confirm the slice has a valid divergence
point before the resolver returns the dependency-derived
parent branch. A ``None`` result signals "no fork point" and
the resolver routes onto ``pipeline_branch`` (the safe
root-stack fallback).
General ancestry/fork-point primitive. (Slice-4 TASK-4-3
once wired this into ``_resolve_slice_base_branch`` to
validate a slice's fork point, but #2928 replaced that with a
parent-branch-existence probe — probing the slice's own
not-yet-created integration branch mis-based fresh slices. The
method is retained as a general gateway utility.)
"""
if not ref_a or not ref_b:
return None
Expand Down Expand Up @@ -3064,24 +3061,23 @@ def _do_fetch() -> dict[str, Any]:
except Exception:
pass

def ls_remote_branch(
def _ls_remote_branch_impl(
self,
pipeline_id: str,
repo_path: str,
ref: str,
mode: Literal["public", "private"] = "public",
mode: Literal["public", "private"],
container_id_suffix: str,
) -> bool:
"""Check if a remote branch exists using ls-remote.

Args:
pipeline_id: Pipeline ID (used as container_id for the temp session)
repo_path: Path to the repo directory
ref: Branch ref to check (e.g., "refs/heads/egg/pipeline-state")
"""Shared implementation of the ls-remote branch-existence probe.

Returns:
True if the remote branch exists, False otherwise
Raises on any gateway / network / policy failure — including a
``{"success": false, ...}`` envelope returned at HTTP 200. Public
wrappers apply their respective error policies at the outer
layer: :meth:`ls_remote_branch` swallows and returns ``False``;
:meth:`ls_remote_branch_strict` propagates.
"""
temp_container_id = f"{pipeline_id}-state-ls-remote"
temp_container_id = f"{pipeline_id}-{container_id_suffix}"
session_token: str | None = None
try:
session = self.register_session(
Expand All @@ -3108,9 +3104,57 @@ def ls_remote_branch(
bearer_token=session_token,
)

# A {"success": false, ...} envelope returned at HTTP 200 is
# a gateway-side failure surfaced via the envelope rather
# than the status code. Without this guard the strict
# variant would silently collapse such a response to "branch
# absent", contradicting its propagate-any-failure contract.
if not result.get("success", True):
raise GatewayError(
result.get("message", "ls-remote envelope reported success=false")
)

# ls-remote returns output in data.stdout; non-empty means branch exists
stdout = result.get("data", {}).get("stdout", "")
return bool(stdout.strip())
finally:
if session_token:
try:
self.delete_session(session_token)
except Exception:
pass

def ls_remote_branch(
self,
pipeline_id: str,
repo_path: str,
ref: str,
mode: Literal["public", "private"] = "public",
) -> bool:
"""Check if a remote branch exists using ls-remote.

Lenient variant: collapses gateway / network / policy failures
to ``False``. Callers that need to distinguish "branch absent
on origin" from "probe could not be performed" — notably the
``_resolve_slice_base_branch`` parent-existence gate (#2928) —
must use :meth:`ls_remote_branch_strict` instead.

Args:
pipeline_id: Pipeline ID (used as container_id for the temp session)
repo_path: Path to the repo directory
ref: Branch ref to check (e.g., "refs/heads/egg/pipeline-state")

Returns:
True if the remote branch exists, False otherwise (or on error).
"""
try:
return self._ls_remote_branch_impl(
pipeline_id=pipeline_id,
repo_path=repo_path,
ref=ref,
mode=mode,
container_id_suffix="state-ls-remote",
)
except Exception as e:
logger.warning(
"ls-remote check failed",
Expand All @@ -3119,12 +3163,34 @@ def ls_remote_branch(
error=str(e),
)
return False
finally:
if session_token:
try:
self.delete_session(session_token)
except Exception:
pass

def ls_remote_branch_strict(
self,
pipeline_id: str,
repo_path: str,
ref: str,
mode: Literal["public", "private"] = "public",
) -> bool:
"""Check if a remote branch exists using ls-remote.

Strict variant of :meth:`ls_remote_branch`: a gateway / network
/ policy failure RAISES rather than collapsing to ``False``.
Use this when the caller needs to distinguish "branch absent
on origin" from "probe could not be performed" — for example,
``_resolve_slice_base_branch`` (#2928) routes a confirmed
absent parent onto ``pipeline_branch`` but treats a raised
probe as "assume parent exists" so a flaky gateway never
silently swaps a real slice onto ``work``. The lenient
:meth:`ls_remote_branch` collapses those two outcomes and is
unsafe for that gate.
"""
return self._ls_remote_branch_impl(
pipeline_id=pipeline_id,
repo_path=repo_path,
ref=ref,
mode=mode,
container_id_suffix="state-ls-remote-strict",
)

def get_remote_branch_sha(
self,
Expand Down
Loading
Loading