From 1a7a563194ddba15daf3cd0d3e11bdbfa0501abd Mon Sep 17 00:00:00 2001 From: Rahul Varghese Date: Mon, 3 Aug 2026 22:27:23 +0530 Subject: [PATCH] =?UTF-8?q?fix(agent):=20ground=20empty=20remotes=20?= =?UTF-8?q?=E2=80=94=20unborn=20HEAD=20at=20the=20ref,=20not=20a=20dead=20?= =?UTF-8?q?clone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A zero-commit product repo is the blueprint bootstrap's normal starting point ("an empty repo is ideal"), but grounding cloned with `--branch `, which cannot match on a refless remote — the runtime died at repo_clone_failed before dialing home and the session hung at "Agent coming online" (observed live on sourceplane/nexara). Now a failed ref-pinned clone is confirmed against the remote (ls-remote through the same credential helper; any listing failure keeps the original clone error), then recloned refless with the unborn HEAD aimed at the bound ref — the first commit is born on the branch the platform expects pushed. Task branches start unborn from unborn history instead of failing on a start point that cannot resolve. Co-Authored-By: Claude Opus 5 --- internal/agent/ground/ground.go | 52 ++++++++++++++++++++++++++-- internal/agent/ground/ground_test.go | 50 ++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/internal/agent/ground/ground.go b/internal/agent/ground/ground.go index ff30036..a7f3888 100644 --- a/internal/agent/ground/ground.go +++ b/internal/agent/ground/ground.go @@ -148,7 +148,32 @@ func Ground(ctx context.Context, cfg *Config, opts Options) (string, error) { logf(opts.Log, "orun agent serve: grounding — cloning %s (blobless) at %s into %s\n", redactCredentials(cfg.Remote), refOrDefault(cfg.Ref), dir) if _, err := runGit(ctx, "", args...); err != nil { - return "", stageErr(StageClone, err) + // A zero-commit repository has no refs, so `--branch ` cannot + // match — and a fresh product repo is the blueprint bootstrap's + // normal starting point ("an empty repo is ideal"). Confirm the + // remote really is refless before rerouting; any other clone + // failure keeps its original error. + if cfg.Ref == "" || !remoteIsEmpty(ctx, cfg.Remote, opts.CredentialHelper) { + return "", stageErr(StageClone, err) + } + logf(opts.Log, "orun agent serve: grounding — %s is an empty repository, cloning refless and starting %s unborn\n", + redactCredentials(cfg.Remote), cfg.Ref) + // git removes the clone target on failure; clear defensively in + // case a partial dir survived, then clone without the ref pin. + _ = os.RemoveAll(dir) + args = []string{"clone", "--filter=blob:none"} + if opts.CredentialHelper != "" { + args = append(args, "--config", "credential.helper="+opts.CredentialHelper) + } + args = append(args, cfg.Remote, dir) + if _, err := runGit(ctx, "", args...); err != nil { + return "", stageErr(StageClone, err) + } + // Aim the unborn HEAD at the bound ref so the very first commit is + // born on the branch the platform expects to see pushed. + if _, err := runGit(ctx, dir, "symbolic-ref", "HEAD", "refs/heads/"+cfg.Ref); err != nil { + return "", stageErr(StageClone, err) + } } } @@ -163,7 +188,10 @@ func Ground(ctx context.Context, cfg *Config, opts Options) (string, error) { logf(opts.Log, "orun agent serve: grounding — task branch %s exists, reused\n", opts.Branch) } else { args := []string{"checkout", "-b", opts.Branch} - if cfg.Ref != "" { + // The start point must resolve locally; on an empty remote the + // bound ref is an unborn HEAD, and the task branch starts unborn + // from it. (A bad ref on a real repo already failed the clone.) + if cfg.Ref != "" && refResolves(ctx, dir, cfg.Ref) { args = append(args, cfg.Ref) } if _, err := runGit(ctx, dir, args...); err != nil { @@ -212,6 +240,26 @@ func branchExists(ctx context.Context, dir, branch string) bool { return err == nil } +// refResolves reports whether the ref names a commit in the local clone. +func refResolves(ctx context.Context, dir, ref string) bool { + _, err := runGit(ctx, dir, "rev-parse", "--verify", "--quiet", ref+"^{commit}") + return err == nil +} + +// remoteIsEmpty reports whether the remote has no refs at all — a freshly +// created repository. The listing rides the same credential helper as the +// clone; any listing failure reads as NOT empty, so the clone's own error +// stays the one reported. +func remoteIsEmpty(ctx context.Context, remote, credentialHelper string) bool { + var args []string + if credentialHelper != "" { + args = append(args, "-c", "credential.helper="+credentialHelper) + } + args = append(args, "ls-remote", "--heads", "--tags", remote) + out, err := runGit(ctx, "", args...) + return err == nil && strings.TrimSpace(out) == "" +} + // runGit runs `git -C ` (plain `git ` when dir is empty — // the clone creates its own directory) and returns trimmed stdout. A failure // carries git's trimmed stderr, credential-redacted; stage tagging is the diff --git a/internal/agent/ground/ground_test.go b/internal/agent/ground/ground_test.go index 04fac88..eb85785 100644 --- a/internal/agent/ground/ground_test.go +++ b/internal/agent/ground/ground_test.go @@ -316,3 +316,53 @@ func assertStage(t *testing.T, err error, stage string) { t.Errorf("error %q lacks the terminal marker", err.Error()) } } + +// newEmptyRemote builds a bare repo with no commits at all — the shape of a +// product repo the instant GitHub creates it, and the blueprint bootstrap's +// normal starting point ("an empty repo is ideal"). +func newEmptyRemote(t *testing.T) string { + t.Helper() + requireGit(t) + bare := filepath.Join(t.TempDir(), "empty.git") + mustGit(t, "", "init", "--quiet", "--bare", "--initial-branch=main", bare) + return bare +} + +func TestGroundEmptyRemoteStartsUnbornAtRef(t *testing.T) { + // A zero-commit remote has no refs for `--branch` to match; grounding must + // still come up, with HEAD unborn at the bound ref so the first commit is + // born on the branch the platform expects pushed. (Observed live: the + // blueprint bootstrap on a fresh repo died at clone and the session hung + // at "Agent coming online".) + remote := newEmptyRemote(t) + cfg := &Config{Remote: remote, FullName: "acme/newborn", Ref: "main"} + dir, err := Ground(context.Background(), cfg, Options{WorkdirRoot: t.TempDir()}) + if err != nil { + t.Fatalf("Ground on an empty remote: %v", err) + } + if head := mustGit(t, dir, "symbolic-ref", "HEAD"); head != "refs/heads/main" { + t.Errorf("HEAD = %q, want refs/heads/main (unborn)", head) + } + // The workflow's first commit + push must land on main upstream. + mustGit(t, dir, "config", "user.email", "test@example.com") + mustGit(t, dir, "config", "user.name", "Test") + mustGit(t, dir, "commit", "--quiet", "--allow-empty", "-m", "first") + mustGit(t, dir, "push", "--quiet", "origin", "main") + if got := mustGit(t, "", "-C", remote, "rev-parse", "--abbrev-ref", "HEAD"); got != "main" { + t.Errorf("remote HEAD = %q, want main", got) + } +} + +func TestGroundEmptyRemoteCreatesUnbornTaskBranch(t *testing.T) { + // Task runs carry a branch; on unborn history it starts unborn too rather + // than failing on a start point that cannot resolve. + remote := newEmptyRemote(t) + cfg := &Config{Remote: remote, FullName: "acme/newborn", Ref: "main"} + dir, err := Ground(context.Background(), cfg, Options{Branch: "agent/ORN-9-implementer", WorkdirRoot: t.TempDir()}) + if err != nil { + t.Fatalf("Ground: %v", err) + } + if head := mustGit(t, dir, "symbolic-ref", "HEAD"); head != "refs/heads/agent/ORN-9-implementer" { + t.Errorf("HEAD = %q, want the unborn task branch", head) + } +}