# Files changed · PR \#22942 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/22942/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[fix\(server\): fall back when the recorded PR base is unavailable remotely](https://git.cafe/pingdotgg/t3code/pull/22942.md)

Version: 29

Head commit: e90dbdbae28233cca48fd270c0be43a3d3e1b7f0

Base commit: 8d3b061121fe65669d5d43d011b48cd3027a4108

Comparison base: 8d3b061121fe65669d5d43d011b48cd3027a4108

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/server/scripts/evidence/pr-22943/bin/codex

Status: added; +20; −0

```diff
@@ -1,0 +1,20 @@
+#!/bin/bash
+# Fake codex: answers the probes T3 makes and writes a fixed PR title/body for `exec`.
+dir="${EV_LOG_DIR:-/tmp}"
+echo "codex $*" >> "$dir/codex.log"
+case "$1" in
+  --version) echo "codex-cli 0.0.0-fake"; exit 0 ;;
+  mcp) echo "[]"; exit 0 ;;
+  exec)
+    out=""
+    prev=""
+    for arg in "$@"; do
+      if [ "$prev" = "--output-last-message" ] || [ "$prev" = "-o" ]; then out="$arg"; fi
+      prev="$arg"
+    done
+    n=$(ls "$dir"/prompt-*.txt 2>/dev/null | wc -l)
+    cat > "$dir/prompt-$((n + 1)).txt"
+    if [ -n "$out" ]; then printf '%s' '{"title":"Fixture PR","body":"fixture body"}' > "$out"; fi
+    exit 0 ;;
+esac
+exit 0
```

## apps/server/scripts/evidence/pr-22943/bin/gh

Status: added; +5; −0

```diff
@@ -1,0 +1,5 @@
+#!/bin/sh
+# Fake gh: only hands out a token for the stub forge. Logs argv.
+echo "gh $*" >> "${EV_LOG_DIR:-/tmp}/gh.log"
+if [ "$1" = "auth" ] && [ "$2" = "token" ]; then echo fake-token; exit 0; fi
+exit 1
```

## apps/server/scripts/evidence/pr-22943/fixture.sh

Status: added; +27; −0

```diff
@@ -1,0 +1,27 @@
+#!/bin/bash
+# Builds one row's git fixture: a local bare repo acting as the remote, and a work clone
+# whose origin URL is https://github.invalid/acme/demo.git (pushes rewritten to the bare repo).
+# Usage: fixture.sh <row-dir> <R1|R2a|R2b|R3|R4a|R4b>
+set -euo pipefail
+R=$1; ROW=$2
+B=$R/remote.git; W=$R/work; U=https://github.invalid/acme/demo.git
+export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1
+rm -rf "$R"; mkdir -p "$R"
+DEF=main; case $ROW in R4*) DEF=trunk ;; esac
+git init -q --bare -b main "$B"
+git init -q -b "$DEF" "$W"; cd "$W"
+git config user.name Fixture; git config user.email fixture@example.invalid
+git remote add origin "$U"
+git config url."$B".pushInsteadOf "$U"
+F() { git -c url."$B".insteadOf="$U" "$@"; }
+echo m > m; git add m; git commit -qm "Main commit"; git push -q -u origin "$DEF"
+case $ROW in R4*) ;; *) git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main ;; esac
+git switch -qc parent; echo p > p; git add p; git commit -qm "Parent commit"
+case $ROW in R1|R2a|R2b) git push -q -u origin parent ;; esac
+git switch -qc child; echo c > c; git add c; git commit -qm "Child commit"; git push -q -u origin child
+git config branch.child.gh-merge-base parent
+case $ROW in
+  R2a) git -C "$B" branch -q -D parent; F fetch -q --prune origin ;;
+  R2b) git -C "$B" branch -q -D parent ;;
+  R4b) git config --unset branch.child.gh-merge-base ;;
+esac
```

## apps/server/scripts/evidence/pr-22943/forge-stub.mjs

Status: added; +187; −0

```diff
@@ -1,0 +1,187 @@
+// HTTPS CONNECT proxy that only admits github.invalid:443 and serves a minimal GitHub
+// Enterprise API there. Every request (and every refused CONNECT) is appended to a JSONL log.
+import * as NodeChildProcess from "node:child_process";
+import * as NodeFS from "node:fs";
+import * as NodeHttp from "node:http";
+import * as NodeHttps from "node:https";
+import * as NodePath from "node:path";
+
+const HOST = "github.invalid";
+
+export function ensureCertificates(caDir) {
+  const leafKey = NodePath.join(caDir, "leaf.key");
+  if (NodeFS.existsSync(leafKey)) return;
+  NodeFS.mkdirSync(caDir, { recursive: true });
+  const openssl = (...args) => NodeChildProcess.execFileSync("openssl", args, { stdio: "pipe" });
+  openssl(
+    "req",
+    "-x509",
+    "-newkey",
+    "rsa:2048",
+    "-nodes",
+    "-days",
+    "2",
+    "-subj",
+    "/CN=pr-22943 throwaway CA",
+    "-keyout",
+    NodePath.join(caDir, "ca.key"),
+    "-out",
+    NodePath.join(caDir, "ca.pem"),
+  );
+  NodeFS.writeFileSync(NodePath.join(caDir, "leaf.ext"), `subjectAltName=DNS:${HOST}\n`);
+  openssl(
+    "req",
+    "-newkey",
+    "rsa:2048",
+    "-nodes",
+    "-subj",
+    `/CN=${HOST}`,
+    "-keyout",
+    leafKey,
+    "-out",
+    NodePath.join(caDir, "leaf.csr"),
+  );
+  openssl(
+    "x509",
+    "-req",
+    "-in",
+    NodePath.join(caDir, "leaf.csr"),
+    "-CA",
+    NodePath.join(caDir, "ca.pem"),
+    "-CAkey",
+    NodePath.join(caDir, "ca.key"),
+    "-CAcreateserial",
+    "-days",
+    "2",
+    "-extfile",
+    NodePath.join(caDir, "leaf.ext"),
+    "-out",
+    NodePath.join(caDir, "leaf.pem"),
+  );
+}
+
+/** @param {{ caDir: string, logFile: string, repoInfo: "ok" | "404" }} options */
+export async function startForgeStub({ caDir, logFile, repoInfo }) {
+  ensureCertificates(caDir);
+  const log = (entry) =>
+    NodeFS.appendFileSync(
+      logFile,
+      JSON.stringify({ at: new Date().toISOString(), ...entry }) + "\n",
+    );
+  let created = null;
+
+  const api = NodeHttps.createServer(
+    {
+      key: NodeFS.readFileSync(NodePath.join(caDir, "leaf.key")),
+      cert: NodeFS.readFileSync(NodePath.join(caDir, "leaf.pem")),
+    },
+    (req, res) => {
+      let raw = "";
+      req.on("data", (chunk) => (raw += chunk));
+      req.on("end", () => {
+        let body = null;
+        try {
+          body = raw ? JSON.parse(raw) : null;
+        } catch {
+          body = raw;
+        }
+        const json = (status, value) => {
+          log({ kind: "request", method: req.method, url: req.url, body, status, response: value });
+          res.writeHead(status, { "content-type": "application/json" }).end(JSON.stringify(value));
+        };
+        const path = (req.url ?? "").split("?")[0];
+        if (req.method === "GET" && path === "/api/v3/repos/acme/demo") {
+          return repoInfo === "404"
+            ? json(404, { message: "Not Found" })
+            : json(200, {
+                id: 1,
+                node_id: "R_1",
+                name: "demo",
+                full_name: "acme/demo",
+                private: false,
+                owner: { login: "acme", type: "Organization" },
+                html_url: `https://${HOST}/acme/demo`,
+                clone_url: `https://${HOST}/acme/demo.git`,
+                ssh_url: `git@${HOST}:acme/demo.git`,
+                default_branch: "main",
+              });
+        }
+        if (req.method === "POST" && path === "/api/graphql") {
+          const query = String(body?.query ?? "");
+          if (query.includes("pullRequests(")) {
+            // Batched queries alias each field as `_<n>_repository` with `_<n>_head` variables.
+            const prefixes = [
+              ...query.matchAll(/(_\d+_)?repository:repository\(|repository\(/g),
+            ].map((match) => match[1] ?? "");
+            const data = {};
+            for (const prefix of new Set(prefixes)) {
+              const head = body?.variables?.[`${prefix}head`];
+              const nodes = created && created.head === head ? [created.node] : [];
+              data[`${prefix}repository`] = {
+                pullRequests: { pageInfo: { hasNextPage: false, endCursor: null }, nodes },
+              };
+            }
+            return json(200, { data });
+          }
+          return json(200, { errors: [{ message: "stub: unsupported query" }] });
+        }
+        if (req.method === "POST" && path === "/api/v3/repos/acme/demo/pulls") {
+          const node = {
+            number: 7,
+            title: body.title,
+            url: `https://${HOST}/acme/demo/pull/7`,
+            baseRefName: body.base,
+            headRefName: body.head,
+            headRefOid: "0".repeat(40),
+            state: "OPEN",
+            isDraft: false,
+            mergedAt: null,
+            closedAt: null,
+            updatedAt: new Date().toISOString(),
+            isCrossRepository: false,
+            headRepository: { name: "demo", nameWithOwner: "acme/demo" },
+            headRepositoryOwner: { __typename: "Organization", login: "acme" },
+          };
+          created = { head: body.head, node };
+          log({ kind: "forge-create-pr", body });
+          return json(201, {
+            number: 7,
+            html_url: node.url,
+            base: { ref: body.base },
+            head: { ref: body.head },
+            title: body.title,
+          });
+        }
+        return json(404, { message: "stub: not handled" });
+      });
+    },
+  );
+  api.on("tlsClientError", (error) => log({ kind: "tls-error", message: error.message }));
+
+  const proxy = NodeHttp.createServer((req, res) => {
+    log({ kind: "plain-http-refused", method: req.method, url: req.url });
+    res.writeHead(403).end();
+  });
+  proxy.on("connect", (req, socket, head) => {
+    if (req.url !== `${HOST}:443`) {
+      log({ kind: "connect-refused", target: req.url });
+      socket.end("HTTP/1.1 403 Forbidden\r\n\r\n");
+      return;
+    }
+    log({ kind: "connect", target: req.url });
+    socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
+    if (head.length > 0) socket.unshift(head);
+    api.emit("connection", socket);
+  });
+  proxy.listen(0, "127.0.0.1");
+  await new Promise((done) => proxy.once("listening", done));
+  return {
+    port: proxy.address().port,
+    close: () =>
+      new Promise((done) => {
+        proxy.closeAllConnections?.();
+        api.closeAllConnections?.();
+        proxy.close(() => done());
+      }),
+  };
+}
```

## apps/server/scripts/evidence/pr-22943/run.mjs

Status: added; +282; −0

```diff
@@ -1,0 +1,282 @@
+// Evidence driver for PR #22943 (issue #22920): runs the real T3 server from <server-checkout>
+// once per fixture row and drives git.runStackedAction create_pr against a stubbed forge.
+//   node apps/server/scripts/evidence/pr-22943/run.mjs --server <checkout> --out <dir> [--rows R1,R3]
+//   node apps/server/scripts/evidence/pr-22943/run.mjs --smoke --out <dir>
+import * as NodeAssert from "node:assert/strict";
+import * as NodeChildProcess from "node:child_process";
+import * as NodeCrypto from "node:crypto";
+import * as NodeFS from "node:fs";
+import * as NodeHttp from "node:http";
+import * as NodePath from "node:path";
+import * as NodeUtil from "node:util";
+import * as Effect from "effect/Effect";
+import * as Exit from "effect/Exit";
+import * as Layer from "effect/Layer";
+import * as Stream from "effect/Stream";
+import { Socket } from "effect/socket";
+import { RpcClient, RpcSerialization } from "effect/rpc";
+import {
+  ORCHESTRATION_PROTOCOL_HEADER,
+  ORCHESTRATION_PROTOCOL_VERSION,
+  WsRpcGroup,
+} from "@t3tools/contracts";
+import { startForgeStub } from "./forge-stub.mjs";
+
+const here = import.meta.dirname;
+const { values } = NodeUtil.parseArgs({
+  options: {
+    server: { type: "string" },
+    out: { type: "string" },
+    rows: { type: "string", default: "R1,R2a,R2b,R3,R4a,R4b" },
+    smoke: { type: "boolean", default: false },
+  },
+});
+NodeAssert.ok(values.out, "--out is required");
+const out = NodePath.resolve(values.out);
+const caDir = NodePath.join(out, "ca");
+NodeFS.mkdirSync(out, { recursive: true });
+
+function isolatedEnv(logDir, stubPort) {
+  const fakeHome = NodePath.join(out, "fakehome");
+  NodeFS.mkdirSync(fakeHome, { recursive: true });
+  const gitconfig = NodePath.join(out, "gitconfig");
+  NodeFS.writeFileSync(gitconfig, "");
+  return {
+    PATH: `${NodePath.join(here, "bin")}:/usr/bin:/bin`,
+    HOME: fakeHome,
+    GIT_CONFIG_GLOBAL: gitconfig,
+    GIT_CONFIG_NOSYSTEM: "1",
+    GIT_TERMINAL_PROMPT: "0",
+    NODE_USE_ENV_PROXY: "1",
+    HTTPS_PROXY: `http://127.0.0.1:${stubPort}`,
+    HTTP_PROXY: `http://127.0.0.1:${stubPort}`,
+    NO_PROXY: "127.0.0.1,localhost",
+    NODE_EXTRA_CA_CERTS: NodePath.join(caDir, "ca.pem"),
+    EV_LOG_DIR: logDir,
+  };
+}
+
+if (values.smoke) {
+  const logDir = NodePath.join(out, "smoke");
+  NodeFS.mkdirSync(logDir, { recursive: true });
+  const stub = await startForgeStub({
+    caDir,
+    logFile: NodePath.join(logDir, "forge.jsonl"),
+    repoInfo: "ok",
+  });
+  const probe = `for (const u of ["https://github.invalid/api/v3/repos/acme/demo","https://example.com/"]) {
+    try { const r = await fetch(u); console.log(u, r.status, (await r.text()).slice(0, 80)); }
+    catch (e) { console.log(u, "ERR", e.cause?.message ?? e.message); } }`;
+  // Async spawn: the stub lives in this process and must keep serving while the probe runs.
+  const probeChild = NodeChildProcess.spawn(
+    process.execPath,
+    ["--input-type=module", "-e", probe],
+    {
+      env: isolatedEnv(logDir, stub.port),
+      stdio: ["ignore", "inherit", "inherit"],
+    },
+  );
+  await new Promise((done) => probeChild.once("exit", done));
+  console.log(NodeFS.readFileSync(NodePath.join(logDir, "forge.jsonl"), "utf8"));
+  await stub.close();
+  process.exit(0);
+}
+
+NodeAssert.ok(values.server, "--server is required");
+const serverRoot = NodePath.resolve(values.server);
+const git = (cwd, ...args) =>
+  NodeChildProcess.execFileSync("git", args, {
+    cwd,
+    encoding: "utf8",
+    env: { ...process.env, GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_NOSYSTEM: "1" },
+  });
+const serverSha = git(serverRoot, "rev-parse", "HEAD").trim();
+
+function dumpRefs(rowDir) {
+  const W = NodePath.join(rowDir, "work");
+  const B = NodePath.join(rowDir, "remote.git");
+  const tryGit = (cwd, ...args) => {
+    try {
+      return git(cwd, ...args).trim();
+    } catch {
+      return "(none)";
+    }
+  };
+  return [
+    "# bare remote (local bare repo acting as the remote): for-each-ref",
+    tryGit(B, "for-each-ref", "--format=%(objectname:short) %(refname)"),
+    `# bare remote HEAD: ${tryGit(B, "symbolic-ref", "HEAD")}`,
+    "# work: for-each-ref refs/heads refs/remotes",
+    tryGit(
+      W,
+      "for-each-ref",
+      "--format=%(objectname:short) %(refname)",
+      "refs/heads",
+      "refs/remotes",
+    ),
+    `# work: symbolic-ref refs/remotes/origin/HEAD: ${tryGit(W, "symbolic-ref", "-q", "refs/remotes/origin/HEAD")}`,
+    "# work: branch.child.* config",
+    tryGit(W, "config", "--get-regexp", "^branch\\.child\\."),
+  ].join("\n");
+}
+
+async function freePort() {
+  const server = NodeHttp.createServer().listen(0, "127.0.0.1");
+  await new Promise((done) => server.once("listening", done));
+  const { port } = server.address();
+  await new Promise((done) => server.close(() => done()));
+  return port;
+}
+
+async function runRow(row) {
+  const rowDir = NodePath.join(out, row);
+  NodeChildProcess.execFileSync(NodePath.join(here, "fixture.sh"), [rowDir, row], {
+    stdio: "pipe",
+  });
+  const logDir = NodePath.join(rowDir, "logs");
+  NodeFS.mkdirSync(logDir, { recursive: true });
+  const work = NodePath.join(rowDir, "work");
+  const refsBefore = dumpRefs(rowDir);
+  NodeFS.writeFileSync(NodePath.join(logDir, "refs-before.txt"), refsBefore + "\n");
+
+  const stub = await startForgeStub({
+    caDir,
+    logFile: NodePath.join(logDir, "forge.jsonl"),
+    repoInfo: row.startsWith("R4") ? "404" : "ok",
+  });
+  const port = await freePort();
+  const origin = `http://127.0.0.1:${port}`;
+  const bootstrap = NodeCrypto.randomUUID();
+  const home = NodePath.join(rowDir, "t3home");
+  const server = NodeChildProcess.spawn(
+    process.execPath,
+    ["apps/server/src/bin.ts", "start", "--bootstrap-fd", "3", "--log-level", "debug"],
+    {
+      cwd: serverRoot,
+      env: isolatedEnv(logDir, stub.port),
+      stdio: ["ignore", "pipe", "pipe", "pipe"],
+    },
+  );
+  server.stdio[3].end(
+    JSON.stringify({
+      mode: "desktop",
+      noBrowser: true,
+      port,
+      t3Home: home,
+      host: "127.0.0.1",
+      desktopBootstrapToken: bootstrap,
+      tailscaleServeEnabled: false,
+      tailscaleServePort: 443,
+      // Skip login-shell PATH hydration, which would put the machine's real codex/gh ahead of the fakes.
+      shellEnvironmentPrepared: true,
+    }),
+  );
+  const ready = Promise.withResolvers();
+  let tail = "";
+  for (const pipe of [server.stdout, server.stderr])
+    pipe.on("data", (data) => {
+      const text = data.toString().replaceAll(bootstrap, "[REDACTED]");
+      NodeFS.appendFileSync(NodePath.join(logDir, "server.log"), text);
+      tail = (tail + text).slice(-8192);
+      if (tail.includes("startup phase: complete")) ready.resolve();
+    });
+  server.once("exit", (code) => ready.reject(new Error(`server exited ${code}`)));
+  const deadline = setTimeout(() => ready.reject(new Error("server startup timed out")), 90_000);
+
+  const events = [];
+  let outcome;
+  try {
+    await ready.promise;
+    clearTimeout(deadline);
+    const tokenResponse = await fetch(`${origin}/oauth/token`, {
+      method: "POST",
+      body: new URLSearchParams({
+        grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
+        subject_token: bootstrap,
+        subject_token_type: "urn:t3:params:oauth:token-type:environment-bootstrap",
+        requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
+      }),
+    });
+    NodeAssert.ok(tokenResponse.ok, `token exchange failed: ${tokenResponse.status}`);
+    const { access_token } = await tokenResponse.json();
+    const ticketResponse = await fetch(`${origin}/api/auth/websocket-ticket`, {
+      method: "POST",
+      headers: {
+        Authorization: `Bearer ${access_token}`,
+        [ORCHESTRATION_PROTOCOL_HEADER]: String(ORCHESTRATION_PROTOCOL_VERSION),
+      },
+    });
+    NodeAssert.ok(ticketResponse.ok, `websocket ticket failed: ${ticketResponse.status}`);
+    const { ticket } = await ticketResponse.json();
+    const layer = RpcClient.layerProtocolSocket().pipe(
+      Layer.provide(
+        Socket.layerWebSocket(
+          `${origin.replace("http:", "ws:")}/ws?orchestrationProtocol=${ORCHESTRATION_PROTOCOL_VERSION}&wsTicket=${encodeURIComponent(ticket)}`,
+        ).pipe(Layer.provide(Socket.layerWebSocketConstructorGlobal)),
+      ),
+      Layer.provide(RpcSerialization.layerJson),
+    );
+    const exit = await Effect.runPromiseExit(
+      Effect.gen(function* () {
+        const client = yield* RpcClient.make(WsRpcGroup);
+        yield* client["git.runStackedAction"]({
+          actionId: NodeCrypto.randomUUID(),
+          cwd: work,
+          action: "create_pr",
+        }).pipe(Stream.runForEach((event) => Effect.sync(() => events.push(event))));
+      }).pipe(Effect.scoped, Effect.provide(layer), Effect.timeout("120 seconds")),
+    );
+    outcome = Exit.isSuccess(exit) ? { ok: true } : { ok: false, cause: String(exit.cause) };
+  } catch (error) {
+    outcome = { ok: false, cause: String(error?.stack ?? error) };
+  } finally {
+    clearTimeout(deadline);
+    if (server.exitCode === null) {
+      const exited = new Promise((done) => server.once("exit", done));
+      server.kill("SIGTERM");
+      const forced = setTimeout(() => server.kill("SIGKILL"), 10_000);
+      await exited;
+      clearTimeout(forced);
+    }
+    await stub.close();
+  }
+
+  const refsAfter = dumpRefs(rowDir);
+  NodeFS.writeFileSync(NodePath.join(logDir, "refs-after.txt"), refsAfter + "\n");
+  NodeFS.writeFileSync(NodePath.join(logDir, "events.json"), JSON.stringify(events, null, 2));
+  const forge = NodeFS.readFileSync(NodePath.join(logDir, "forge.jsonl"), "utf8")
+    .split("\n")
+    .filter(Boolean)
+    .map((line) => JSON.parse(line));
+  const posts = forge
+    .filter((entry) => entry.kind === "forge-create-pr")
+    .map((entry) => entry.body);
+  const promptFiles = NodeFS.readdirSync(logDir).filter((name) => name.startsWith("prompt-"));
+  const promptRange = promptFiles.map((name) => {
+    const prompt = NodeFS.readFileSync(NodePath.join(logDir, name), "utf8");
+    const base = prompt.match(/^Base branch: .*$/m)?.[0] ?? null;
+    const commits = prompt.match(/^Commits:\n([\s\S]*?)\n\n/m)?.[1]?.split("\n") ?? [];
+    return { file: name, base, commits };
+  });
+  const summary = {
+    row,
+    serverSha,
+    posts,
+    promptRange,
+    phaseLabels: events
+      .filter((e) => e.kind === "phase_started")
+      .map((e) => `${e.phase}: ${e.label}`),
+    result: events.find((e) => e.kind === "action_finished")?.result ?? null,
+    failure: events.find((e) => e.kind === "action_failed") ?? null,
+    outcome,
+    refusedConnects: forge.filter((e) => e.kind === "connect-refused").map((e) => e.target),
+    refsUnchanged: refsBefore === refsAfter,
+  };
+  NodeFS.writeFileSync(NodePath.join(logDir, "summary.json"), JSON.stringify(summary, null, 2));
+  return summary;
+}
+
+for (const row of values.rows.split(",")) {
+  const summary = await runRow(row);
+  console.log(JSON.stringify(summary));
+}
```

## apps/server/src/git/GitManager.test.ts

Status: modified; +490; −1

```diff
@@ -743,6 +743,8 @@
 function makeManager(input?: {
   ghScenario?: FakeGhScenario;
   sourceControlProvider?: SourceControlProvider["Service"];
+  sourceControlContext?: SourceControlProviderRegistry.SourceControlProviderHandle["context"];
+  sourceControlHandleError?: SourceControlProviderFailure;
   textGeneration?: Partial<FakeGitTextGeneration>;
   serverSettings?: Parameters<typeof ServerSettings.layerTest>[0];
   setupScriptRunner?: ProjectSetupScriptRunner.ProjectSetupScriptRunner["Service"];
@@ -841,7 +843,10 @@
         SourceControlProviderRegistry.SourceControlProviderRegistry.of({
           resolveLink: (input) => provider.resolveLink?.(input),
           get: () => Effect.succeed(provider),
-          resolveHandle: () => Effect.succeed({ provider, context: null }),
+          resolveHandle: () =>
+            input?.sourceControlHandleError
+              ? Effect.fail(input.sourceControlHandleError)
+              : Effect.succeed({ provider, context: input?.sourceControlContext ?? null }),
           resolve: () => Effect.succeed(provider),
           discover: Effect.succeed([]),
         }),
@@ -3518,6 +3523,490 @@
     }),
   );
 
+  it.effect.each([
+    { baseState: "local-only", t3BaseRef: false },
+    { baseState: "local-only", t3BaseRef: "local" },
+    { baseState: "local-only", t3BaseRef: true },
+    { baseState: "remote", t3BaseRef: "local" },
+    { baseState: "remote", t3BaseRef: false },
+    { baseState: "remote", t3BaseRef: true },
+    { baseState: "unconfigured", t3BaseRef: false },
+    { baseState: "deleted", t3BaseRef: false },
+    { baseState: "deleted", t3BaseRef: true },
+    { baseState: "not-cached", t3BaseRef: false },
+  ] as const)(
+    "resolves PR base against the remote ($baseState, T3 base ref: $t3BaseRef)",
+    ({ baseState, t3BaseRef }) =>
+      Effect.gen(function* () {
+        const repoDir = yield* makeTempDir("t3code-git-manager-");
+        yield* initRepo(repoDir);
+        const remoteDir = yield* createBareRemote();
+        yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]);
+        yield* runGit(repoDir, ["push", "-u", "origin", "main"]);
+        yield* runGit(remoteDir, ["--git-dir=.", "symbolic-ref", "HEAD", "refs/heads/main"]);
+        yield* runGit(repoDir, ["checkout", "-b", "parent"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "parent.txt"), "parent work\n");
+        yield* runGit(repoDir, ["add", "parent.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Parent commit"]);
+        if (baseState === "remote" || baseState === "deleted" || baseState === "not-cached") {
+          yield* runGit(repoDir, ["push", "origin", "parent"]);
+        }
+        yield* runGit(repoDir, ["checkout", "-b", "child"]);
+        if (baseState !== "unconfigured") {
+          yield* runGit(repoDir, ["config", "branch.child.gh-merge-base", "parent"]);
+        }
+        if (t3BaseRef) {
+          // T3-created remote-based worktrees also preserve the qualified comparison ref.
+          yield* runGit(repoDir, [
+            "config",
+            "branch.child.t3-merge-base-ref",
+            t3BaseRef === "local" ? "refs/heads/parent" : "refs/remotes/origin/parent",
+          ]);
+        }
+        NodeFS.writeFileSync(NodePath.join(repoDir, "child.txt"), "child work\n");
+        yield* runGit(repoDir, ["add", "child.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Child commit"]);
+        yield* runGit(repoDir, ["push", "-u", "origin", "child"]);
+        if (baseState === "deleted") {
+          // Delete on the server so the client retains its stale origin/parent until pruning.
+          yield* runGit(remoteDir, ["--git-dir=.", "update-ref", "-d", "refs/heads/parent"]);
+          expect(
+            (yield* runGit(repoDir, ["show-ref", "--verify", "refs/remotes/origin/parent"]))
+              .exitCode,
+          ).toBe(0);
+          yield* runGit(repoDir, ["fetch", "--prune", "origin"]);
+        }
+        if (baseState === "not-cached") {
+          yield* runGit(repoDir, [
+            "config",
+            "--replace-all",
+            "remote.origin.fetch",
+            "+refs/heads/child:refs/remotes/origin/child",
+          ]);
+          yield* runGit(repoDir, ["update-ref", "-d", "refs/remotes/origin/parent"]);
+        }
+        const expectedBase = baseState === "remote" ? "parent" : "main";
+        const missingRemoteBase =
+          baseState === "local-only" || baseState === "deleted" || baseState === "not-cached";
+        expect(
+          (yield* runGit(repoDir, ["show-ref", "--verify", "refs/heads/parent"])).exitCode,
+        ).toBe(0);
+        expect(
+          (yield* runGit(
+            repoDir,
+            ["show-ref", "--verify", "--quiet", "refs/remotes/origin/parent"],
+            true,
+          )).exitCode,
+        ).toBe(baseState === "remote" ? 0 : 1);
+
+        const generatedInputs: TextGeneration.PrContentGenerationInput[] = [];
+        const events: GitActionProgressEvent[] = [];
+        const { manager, ghCalls } = yield* makeManager({
+          ghScenario: { defaultBranch: "main", prListSequence: ["[]", "[]"] },
+          textGeneration: {
+            generatePrContent: (input) => {
+              generatedInputs.push(input);
+              return Effect.succeed({ title: "Child PR", body: "Child body" });
+            },
+          },
+        });
+        const result = yield* runStackedAction(
+          manager,
+          { cwd: repoDir, action: "create_pr" },
+          {
+            progressReporter: {
+              publish: (event) =>
+                Effect.sync(() => {
+                  events.push(event);
+                }),
+            },
+          },
+        );
+
+        expect.soft(result.pr.status).toBe("created");
+        expect.soft(result.pr.baseBranch).toBe(expectedBase);
+        expect(generatedInputs).toHaveLength(1);
+        const generated = generatedInputs[0]!;
+        expect.soft(generated.baseBranch).toBe(expectedBase);
+        expect.soft(generated.commitSummary).toContain("Child commit");
+        expect.soft(generated.diffPatch).toContain("+child work");
+        if (expectedBase === "main") {
+          expect.soft(generated.commitSummary).toContain("Parent commit");
+          expect.soft(generated.diffPatch).toContain("+parent work");
+        } else {
+          expect.soft(generated.commitSummary).not.toContain("Parent commit");
+          expect.soft(generated.diffPatch).not.toContain("+parent work");
+        }
+        expect
+          .soft(ghCalls.filter((call) => call.startsWith("pr create ")))
+          .toEqual([expect.stringContaining(`pr create --base ${expectedBase} --head child`)]);
+        if (missingRemoteBase) {
+          const message =
+            "Base origin/parent is unavailable in this checkout. Using default branch main.";
+          expect.soft(result.toast.description).toContain(message);
+          expect.soft(result.toast.description).not.toContain("refs/remotes/");
+          expect
+            .soft(events)
+            .toContainEqual(
+              expect.objectContaining({ kind: "phase_started", phase: "pr", label: message }),
+            );
+        }
+      }),
+  );
+
+  it.effect.each([false, true])(
+    "creates a PR when optional provider detection fails (parent published: %s)",
+    (published) =>
+      Effect.gen(function* () {
+        const repoDir = yield* makeTempDir("t3code-git-manager-");
+        yield* initRepo(repoDir);
+        const remoteDir = yield* createBareRemote();
+        yield* runGit(repoDir, ["remote", "add", "publish", remoteDir]);
+        yield* runGit(repoDir, ["push", "publish", "main"]);
+        yield* runGit(repoDir, ["checkout", "-b", "parent"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "parent.txt"), "parent work\n");
+        yield* runGit(repoDir, ["add", "parent.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Parent commit"]);
+        if (published) yield* runGit(repoDir, ["push", "publish", "parent"]);
+        yield* runGit(repoDir, ["checkout", "-b", "child"]);
+        yield* runGit(repoDir, ["config", "branch.child.gh-merge-base", "parent"]);
+        yield* runGit(repoDir, ["config", "branch.child.t3-merge-base-ref", "refs/heads/parent"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "child.txt"), "child work\n");
+        yield* runGit(repoDir, ["add", "child.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Child commit"]);
+        yield* runGit(repoDir, ["push", "-u", "publish", "child"]);
+
+        const expectedBase = published ? "parent" : "main";
+        const generatedInputs: TextGeneration.PrContentGenerationInput[] = [];
+        const events: GitActionProgressEvent[] = [];
+        const { manager, ghCalls } = yield* makeManager({
+          sourceControlHandleError: new SourceControlProviderFailure({
+            provider: SourceControlProviderKind.make("unknown"),
+            operation: "detectProvider",
+            cwd: repoDir,
+            detail: "Failed to detect source control provider.",
+          }),
+          textGeneration: {
+            generatePrContent: (input) => {
+              generatedInputs.push(input);
+              return Effect.succeed({ title: "Child PR", body: "Child body" });
+            },
+          },
+        });
+        const result = yield* runStackedAction(
+          manager,
+          { cwd: repoDir, action: "create_pr" },
+          {
+            progressReporter: {
+              publish: (event) =>
+                Effect.sync(() => {
+                  events.push(event);
+                }),
+            },
+          },
+        );
+
+        expect(result.pr.status).toBe("created");
+        expect(result.pr.baseBranch).toBe(expectedBase);
+        expect(generatedInputs).toHaveLength(1);
+        expect(generatedInputs[0]!.baseBranch).toBe(expectedBase);
+        expect(generatedInputs[0]!.commitSummary).toContain("Child commit");
+        expect(generatedInputs[0]!.diffPatch).toContain("+child work");
+        expect(ghCalls.filter((call) => call.startsWith("pr create "))).toEqual([
+          expect.stringContaining(`pr create --base ${expectedBase} --head child`),
+        ]);
+        if (published) {
+          expect(generatedInputs[0]!.commitSummary).not.toContain("Parent commit");
+          expect(generatedInputs[0]!.diffPatch).not.toContain("+parent work");
+          expect(result.toast.description).not.toContain("unavailable");
+        } else {
+          expect(generatedInputs[0]!.commitSummary).toContain("Parent commit");
+          expect(generatedInputs[0]!.diffPatch).toContain("+parent work");
+          const message =
+            "Base publish/parent is unavailable in this checkout. Using default branch main.";
+          expect(result.toast.description).toContain(message);
+          expect(events).toContainEqual(
+            expect.objectContaining({ kind: "phase_started", phase: "pr", label: message }),
+          );
+        }
+      }),
+  );
+
+  it.effect.each([false, true])(
+    "keeps a published non-GitHub base with a bound target context (fork: %s)",
+    (fork) =>
+      Effect.gen(function* () {
+        const repoDir = yield* makeTempDir("t3code-git-manager-");
+        yield* initRepo(repoDir);
+        const originDir = yield* createBareRemote();
+        const publishDir = yield* createBareRemote();
+        yield* runGit(repoDir, ["remote", "add", "origin", originDir]);
+        yield* runGit(repoDir, ["remote", "add", "publish", fork ? publishDir : originDir]);
+        yield* runGit(repoDir, ["push", "origin", "main"]);
+        yield* runGit(repoDir, ["checkout", "-b", "parent"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "parent.txt"), "parent work\n");
+        yield* runGit(repoDir, ["add", "."]);
+        yield* runGit(repoDir, ["commit", "-m", "Parent commit"]);
+        yield* runGit(repoDir, ["push", "origin", "parent"]);
+        yield* runGit(repoDir, ["checkout", "-b", "child"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "child.txt"), "child work\n");
+        yield* runGit(repoDir, ["add", "."]);
+        yield* runGit(repoDir, ["commit", "-m", "Child commit"]);
+        yield* runGit(repoDir, ["push", "-u", "publish", "child"]);
+        yield* runGit(repoDir, ["config", "branch.child.gh-merge-base", "parent"]);
+        // A narrow refspec keeps parent cached only under origin, even after status refreshes.
+        yield* runGit(repoDir, [
+          "config",
+          "remote.publish.fetch",
+          "+refs/heads/child:refs/remotes/publish/child",
+        ]);
+        const { service } = createGitHubProviderWithFakeGh({
+          defaultBranch: "main",
+          prListSequence: ["[]", "[]"],
+        });
+        const kind = SourceControlProviderKind.make("gitcafe");
+        const generatedInputs: TextGeneration.PrContentGenerationInput[] = [];
+        const { manager } = yield* makeManager({
+          sourceControlProvider: { ...service, kind },
+          sourceControlContext: {
+            provider: { kind, name: "GitCafe", baseUrl: "https://git.cafe" },
+            remoteName: "origin",
+            remoteUrl: originDir,
+          },
+          textGeneration: {
+            generatePrContent: (input) => {
+              generatedInputs.push(input);
+              return Effect.succeed({ title: "Child PR", body: "Child body" });
+            },
+          },
+        });
+        const result = yield* runStackedAction(manager, { cwd: repoDir, action: "create_pr" });
+
+        expect(result.pr.baseBranch).toBe("parent");
+        expect(result.toast.description).not.toContain("unavailable");
+        expect(generatedInputs[0]!.commitSummary).toContain("Child commit");
+        expect(generatedInputs[0]!.commitSummary).not.toContain("Parent commit");
+      }),
+  );
+
+  it.effect.each([
+    { remoteConfig: "branch.child.remote", baseRef: null, target: "origin" },
+    { remoteConfig: "branch.child.pushRemote", baseRef: null, target: "origin" },
+    { remoteConfig: "remote.pushDefault", baseRef: null, target: "origin" },
+    { remoteConfig: "branch.child.remote", baseRef: "refs/heads/parent", target: "origin" },
+    { remoteConfig: "branch.child.pushRemote", baseRef: "refs/heads/parent", target: "origin" },
+    { remoteConfig: "remote.pushDefault", baseRef: "refs/heads/parent", target: "origin" },
+    {
+      remoteConfig: "branch.child.pushRemote",
+      baseRef: "refs/remotes/origin/parent",
+      target: "origin",
+    },
+    { remoteConfig: "branch.child.remote", baseRef: null, target: "upstream" },
+    { remoteConfig: "branch.child.remote", baseRef: null, target: "set-default" },
+    { remoteConfig: "branch.child.remote", baseRef: null, target: "environment" },
+    { remoteConfig: "branch.child.remote", baseRef: null, target: "missing" },
+    { remoteConfig: "remote.pushDefault", baseRef: null, target: "alias" },
+    { remoteConfig: "remote.pushDefault", baseRef: null, target: "ssh-alias" },
+    { remoteConfig: "remote.pushDefault", baseRef: null, target: "existing" },
+  ])(
+    "keeps the published head and resolves the target PR base via $remoteConfig ($baseRef, $target)",
+    ({ remoteConfig, baseRef, target }) =>
+      Effect.gen(function* () {
+        const repoDir = yield* makeTempDir("t3code-git-manager-");
+        yield* initRepo(repoDir);
+        const originDir = yield* createBareRemote();
+        const forkDir = yield* createBareRemote();
+        yield* runGit(repoDir, ["remote", "add", "origin", originDir]);
+        yield* runGit(repoDir, ["remote", "add", "fork", forkDir]);
+        yield* runGit(repoDir, ["push", "origin", "main"]);
+        yield* runGit(repoDir, ["checkout", "-b", "parent"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "parent.txt"), "parent work\n");
+        yield* runGit(repoDir, ["add", "parent.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Parent commit"]);
+        yield* runGit(repoDir, ["push", "origin", "parent"]);
+        yield* runGit(repoDir, ["checkout", "-b", "child"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "child.txt"), "child work\n");
+        yield* runGit(repoDir, ["add", "child.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Child commit"]);
+        const trackingRemote = remoteConfig === "branch.child.remote" ? "fork" : "origin";
+        yield* runGit(repoDir, ["push", "-u", trackingRemote, "child"]);
+        expect(
+          (yield* runGit(
+            repoDir,
+            [
+              "show-ref",
+              "--verify",
+              "--quiet",
+              `refs/remotes/${trackingRemote === "fork" ? "origin" : "fork"}/child`,
+            ],
+            true,
+          )).exitCode,
+        ).toBe(1);
+        yield* runGit(repoDir, ["config", remoteConfig, "fork"]);
+        yield* runGit(repoDir, ["config", "branch.child.gh-merge-base", "parent"]);
+        if (baseRef) yield* runGit(repoDir, ["config", "branch.child.t3-merge-base-ref", baseRef]);
+        yield* runGit(repoDir, [
+          "config",
+          "remote.origin.url",
+          target === "ssh-alias"
+            ? "git@github-work:target/project.git"
+            : "git@github.com:target/project.git",
+        ]);
+        yield* runGit(repoDir, ["config", "remote.origin.pushurl", originDir]);
+        yield* runGit(repoDir, [
+          "config",
+          "remote.fork.url",
+          target === "alias"
+            ? "https://github.com/target/project.git"
+            : "git@github.com:contributor/project.git",
+        ]);
+        yield* runGit(repoDir, ["config", "remote.fork.pushurl", forkDir]);
+        if (target === "alias") {
+          yield* runGit(repoDir, ["remote", "rename", "fork", "gh"]);
+          yield* runGit(repoDir, ["config", "remote.gh.gh-resolved", "base"]);
+        }
+        expect(
+          (yield* runGit(
+            repoDir,
+            ["show-ref", "--verify", "--quiet", "refs/remotes/fork/parent"],
+            true,
+          )).exitCode,
+        ).toBe(1);
+        if (target === "upstream")
+          yield* runGit(repoDir, ["remote", "rename", "origin", "upstream"]);
+        if (target === "set-default") {
+          yield* runGit(repoDir, ["remote", "rename", "origin", "destination"]);
+          yield* runGit(repoDir, ["config", "remote.destination.gh-resolved", "base"]);
+        }
+        const generatedInputs: TextGeneration.PrContentGenerationInput[] = [];
+        const { manager, ghCalls } = yield* makeManager({
+          environment:
+            target === "environment" || target === "missing"
+              ? { GH_REPO: target === "missing" ? "elsewhere/project" : "target/project" }
+              : {},
+          ghScenario: {
+            defaultBranch: "main",
+            prListByHeadSelector:
+              target === "existing"
+                ? {
+                    child: JSON.stringify([
+                      {
+                        number: 42,
+                        title: "Existing child",
+                        url: "https://github.com/target/project/pull/42",
+                        baseRefName: "parent",
+                        headRefName: "child",
+                      },
+                    ]),
+                  }
+                : {},
+            prListSequence: ["[]", "[]"],
+          },
+          textGeneration: {
+            generatePrContent: (input) => {
+              generatedInputs.push(input);
+              return Effect.succeed({ title: "Child PR", body: "Child body" });
+            },
+          },
+        });
+        const result = yield* runStackedAction(manager, { cwd: repoDir, action: "create_pr" });
+        if (target === "existing") {
+          expect(result.pr.status).toBe("opened_existing");
+          expect(ghCalls.filter((call) => call.startsWith("pr create "))).toEqual([]);
+          expect(ghCalls.some((call) => call.includes("--head contributor:child"))).toBe(false);
+          return;
+        }
+        expect(result.pr.baseBranch).toBe(target === "missing" ? "main" : "parent");
+        if (target === "missing") {
+          expect(result.toast.description).toContain("unavailable in this checkout");
+          return;
+        }
+        expect(result.toast.description).not.toContain("unavailable in this checkout");
+        expect(generatedInputs).toHaveLength(1);
+        expect(generatedInputs[0]!.commitSummary).toContain("Child commit");
+        expect(generatedInputs[0]!.commitSummary).not.toContain("Parent commit");
+        expect(generatedInputs[0]!.diffPatch).toContain("+child work");
+        expect(generatedInputs[0]!.diffPatch).not.toContain("+parent work");
+        expect(ghCalls.filter((call) => call.startsWith("pr create "))).toEqual([
+          expect.stringContaining(
+            `pr create --base parent --head ${trackingRemote === "fork" ? "contributor:child" : "child"}`,
+          ),
+        ]);
+      }),
+  );
+
+  it.effect.each([
+    { remoteConfig: "branch.child.pushRemote", published: true, remoteName: "publish" },
+    { remoteConfig: "branch.child.pushRemote", published: false, remoteName: "publish" },
+    { remoteConfig: "remote.pushDefault", published: true, remoteName: "publish" },
+    { remoteConfig: "remote.pushDefault", published: false, remoteName: "publish" },
+    { remoteConfig: "branch.child.remote", published: true, remoteName: "publish" },
+    { remoteConfig: "branch.child.remote", published: false, remoteName: "publish" },
+    { remoteConfig: "branch.child.pushRemote", published: false, remoteName: "team/fork" },
+  ])(
+    "validates the recorded PR base on $remoteConfig via $remoteName (published: $published)",
+    ({ remoteConfig, published, remoteName }) =>
+      Effect.gen(function* () {
+        const repoDir = yield* makeTempDir("t3code-git-manager-");
+        yield* initRepo(repoDir);
+        const originDir = yield* createBareRemote();
+        const publishDir = yield* createBareRemote();
+        yield* runGit(repoDir, ["remote", "add", "origin", originDir]);
+        yield* runGit(repoDir, ["remote", "add", remoteName, publishDir]);
+        yield* runGit(repoDir, ["push", "origin", "main"]);
+        yield* runGit(repoDir, ["branch", "parent"]);
+        yield* runGit(repoDir, ["push", "origin", "parent"]);
+        if (published) yield* runGit(repoDir, ["push", remoteName, "parent"]);
+        yield* runGit(repoDir, ["checkout", "-b", "child"]);
+        NodeFS.writeFileSync(NodePath.join(repoDir, "child.txt"), "child work\n");
+        yield* runGit(repoDir, ["add", "child.txt"]);
+        yield* runGit(repoDir, ["commit", "-m", "Child commit"]);
+        yield* runGit(repoDir, [
+          "push",
+          "-u",
+          remoteConfig === "branch.child.remote" ? remoteName : "origin",
+          "child",
+        ]);
+
+        yield* runGit(repoDir, ["config", "branch.child.gh-merge-base", "parent"]);
+        if (remoteConfig === "branch.child.pushRemote") {
+          yield* runGit(repoDir, ["config", "remote.pushDefault", "origin"]);
+        }
+        yield* runGit(repoDir, ["config", remoteConfig, remoteName]);
+        const events: GitActionProgressEvent[] = [];
+        const { manager } = yield* makeManager({
+          ghScenario: { defaultBranch: "main", prListSequence: ["[]", "[]"] },
+        });
+        const result = yield* runStackedAction(
+          manager,
+          { cwd: repoDir, action: "create_pr" },
+          {
+            progressReporter: {
+              publish: (event) =>
+                Effect.sync(() => {
+                  events.push(event);
+                }),
+            },
+          },
+        );
+
+        const usesPublishedRemote = remoteConfig === "branch.child.remote";
+        expect(result.pr.baseBranch).toBe(!usesPublishedRemote || published ? "parent" : "main");
+        if (usesPublishedRemote && !published) {
+          expect(result.toast.description).toContain(
+            `Base ${remoteName}/parent is unavailable in this checkout. Using default branch main.`,
+          );
+          expect(events).toContainEqual(
+            expect.objectContaining({
+              kind: "phase_started",
+              phase: "pr",
+              label: `Base ${remoteName}/parent is unavailable in this checkout. Using default branch main.`,
+            }),
+          );
+        }
+      }),
+  );
+
   it.effect.each(["commit", "short-commit", "tag", "revision"] as const)(
     "creates a PR against the default branch with a legacy %s base setting",
     (kind) =>
```

## apps/server/src/git/GitManager.ts

Status: modified; +166; −59

```diff
@@ -65,6 +65,8 @@
 import { parseChangeRequestUrl } from "@t3tools/shared/changeRequestUrl";
 import * as HostProcess from "@t3tools/shared/HostProcess";
 import {
+  gitHubApiHostForRemote,
+  parseFetchRemotes,
   parseGitHubRepositorySelector,
   resolveGitHubRepository,
 } from "@t3tools/source-control-github/server/gitHubRepositoryResolution";
@@ -2026,41 +2028,130 @@
     upstreamRef: string | null,
     headContext: Pick<BranchHeadContext, "isCrossRepository" | "remoteName">,
   ) {
+    const recordedBase = yield* Effect.gen(function* () {
+      const configuredRef = yield* gitCore.readConfigValue(
+        cwd,
+        `branch.${branch}.t3-merge-base-ref`,
+      );
+      if (configuredRef?.startsWith("refs/heads/") || configuredRef?.startsWith("refs/remotes/")) {
-    const configuredRef = yield* gitCore.readConfigValue(cwd, `branch.${branch}.t3-merge-base-ref`);
+        const configuredBranch = yield* gitCore.resolveBranchName(cwd, configuredRef);
+        return { baseBranch: configuredBranch, baseRef: configuredRef };
+      }
-    if (configuredRef?.startsWith("refs/heads/") || configuredRef?.startsWith("refs/remotes/")) {
-      const configuredBranch = yield* gitCore.resolveBranchName(cwd, configuredRef);
-      if (configuredBranch) return { baseBranch: configuredBranch, baseRef: configuredRef };
-    }
 
+      const configured = yield* gitCore.readConfigValue(cwd, `branch.${branch}.gh-merge-base`);
+      if (configured) {
+        const localRef = configured.startsWith("refs/") ? configured : `refs/heads/${configured}`;
+        const localBranch = yield* gitCore.resolveBranchName(cwd, localRef);
+        if (localBranch) {
+          const shadowsRemote =
+            configured.includes("/") &&
+            !configured.startsWith("refs/") &&
+            (yield* gitCore.resolveBranchName(cwd, `refs/remotes/${configured}`)) !== null;
+          return {
+            baseBranch: localBranch,
+            baseRef: configured.startsWith("refs/") || shadowsRemote ? localRef : localBranch,
+          };
+        }
+        if (configured.includes("/") && !configured.startsWith("refs/")) {
+          const remoteRef = `refs/remotes/${configured}`;
+          const remoteBranch = yield* gitCore.resolveBranchName(cwd, remoteRef);
+          if (remoteBranch) return { baseBranch: remoteBranch, baseRef: remoteRef };
+        }
+        const configuredBranch = yield* gitCore.resolveBranchName(cwd, configured);
+        if (configuredBranch) return { baseBranch: configuredBranch, baseRef: configuredBranch };
+      }
+      return null;
+    });
+    let missingRemoteBase: string | null = null;
+    if (recordedBase) {
+      const publishedRemote =
+        (upstreamRef && headContext.remoteName !== "." ? headContext.remoteName : null) ??
+        (yield* gitCore.readConfigValue(cwd, `branch.${branch}.pushRemote`)) ??
+        (yield* gitCore.readConfigValue(cwd, "remote.pushDefault")) ??
+        headContext.remoteName ??
+        "origin";
+      let baseRemotes = [publishedRemote];
+      let targetRepository: string | null = null;
+      if (!recordedBase.baseRef.startsWith("refs/remotes/")) {
+        const handle = yield* sourceControlProviders
+          .resolveHandle({ cwd })
+          .pipe(Effect.catchTags({ SourceControlProviderError: () => Effect.succeed(null) }));
+        if (handle) {
+          const remotesResult = yield* gitCore.execute({
+            operation: "GitManager.resolvePrBase.remotes",
-    const configured = yield* gitCore.readConfigValue(cwd, `branch.${branch}.gh-merge-base`);
-    if (configured) {
-      const localRef = configured.startsWith("refs/") ? configured : `refs/heads/${configured}`;
-      const localBranch = yield* gitCore.resolveBranchName(cwd, localRef);
-      if (localBranch) {
-        const shadowsRemote =
-          configured.includes("/") &&
-          !configured.startsWith("refs/") &&
+            cwd,
+            args: ["remote", "-v"],
+          });
-          (yield* gitCore.resolveBranchName(cwd, `refs/remotes/${configured}`)) !== null;
-        return {
-          baseBranch: localBranch,
+          const remotes = parseFetchRemotes(remotesResult.stdout);
+          if (handle.provider.kind === "github") {
+            const hostHint = handle.context
+              ? (urlHost(handle.context.provider.baseUrl) ?? undefined)
+              : undefined;
+            const defaultHost = hostHint ?? environment.GH_HOST ?? "github.com";
+            const resolved = yield* gitCore.execute({
+              operation: "GitManager.resolvePrBase.resolved",
+              cwd,
+              args: ["config", "--get-regexp", "^remote\\..*\\.gh-resolved$"],
+              allowNonZeroExit: true,
+            });
+            const target =
+              parseGitHubRepositorySelector(environment.GH_REPO ?? "", defaultHost) ??
+              resolveGitHubRepository({
+                remotes: remotesResult.stdout,
+                resolved: resolved.stdout,
+                hostHint,
+                defaultHost,
+              }).locator;
+            if (target) {
+              targetRepository = `${target.host}/${target.owner}/${target.name}`.toLowerCase();
+            }
+          } else if (handle.context) {
+            targetRepository = normalizeGitRemoteUrl(handle.context.remoteUrl);
+          }
+          if (targetRepository) {
+            baseRemotes = remotes
+              .filter((remote) => {
+                const key = normalizeGitRemoteUrl(remote.url);
+                const host =
+                  handle.provider.kind === "github" ? gitHubApiHostForRemote(remote.url) : null;
+                return (
+                  (host ? `${host}/${key.split("/").slice(1).join("/")}` : key) === targetRepository
+                );
+              })
+              .map((remote) => remote.name)
+              .toSorted((left, right) =>
+                left === publishedRemote ? -1 : right === publishedRemote ? 1 : 0,
+              );
+          }
+        }
-          baseRef: configured.startsWith("refs/") || shadowsRemote ? localRef : localBranch,
-        };
       }
+      const remoteRefs = recordedBase.baseRef.startsWith("refs/remotes/")
+        ? [recordedBase.baseRef]
-      if (configured.includes("/") && !configured.startsWith("refs/")) {
+        : baseRemotes.map(
-        const remoteRef = `refs/remotes/${configured}`;
+            (remote) =>
+              `refs/remotes/${remote}/${recordedBase.baseRef.replace(/^refs\/heads\//, "")}`,
+          );
+      for (const remoteRef of remoteRefs) {
         const remoteBranch = yield* gitCore.resolveBranchName(cwd, remoteRef);
+        if (remoteBranch) {
+          return {
-        if (remoteBranch) return { baseBranch: remoteBranch, baseRef: remoteRef };
+            baseBranch: recordedBase.baseBranch ?? remoteBranch,
+            baseRef: recordedBase.baseBranch ? recordedBase.baseRef : remoteRef,
+            message: null,
+          };
+        }
       }
-      const configuredBranch = yield* gitCore.resolveBranchName(cwd, configured);
-      if (configuredBranch) return { baseBranch: configuredBranch, baseRef: configuredBranch };
+      missingRemoteBase =
+        remoteRefs[0]?.slice("refs/remotes/".length) ??
+        `${targetRepository}/${recordedBase.baseRef.replace(/^refs\/heads\//, "")}`;
     }
 
+    if (!missingRemoteBase && upstreamRef && !headContext.isCrossRepository) {
-    if (upstreamRef && !headContext.isCrossRepository) {
       const upstreamBranch = extractBranchNameFromRemoteRef(upstreamRef, {
         remoteName: headContext.remoteName,
       });
       if (upstreamBranch.length > 0 && upstreamBranch !== branch) {
+        return { baseBranch: upstreamBranch, baseRef: upstreamBranch, message: null };
-        return { baseBranch: upstreamBranch, baseRef: upstreamBranch };
       }
     }
 
@@ -2068,19 +2159,20 @@
       Effect.flatMap((provider) => provider.getDefaultBranch({ cwd })),
       Effect.orElseSucceed(() => null),
     );
-    if (defaultFromProvider) {
-      return { baseBranch: defaultFromProvider, baseRef: defaultFromProvider };
-    }
-
-    const defaultFromRemote = yield* gitCore.resolvePrimaryRemoteName(cwd).pipe(
-      Effect.flatMap((remoteName) => gitCore.resolveDefaultBranchName(cwd, remoteName)),
-      Effect.orElseSucceed(() => null),
-    );
-    if (defaultFromRemote) {
-      return { baseBranch: defaultFromRemote, baseRef: defaultFromRemote };
+    const baseBranch =
-    }
-
-    return { baseBranch: "main", baseRef: "main" };
+      defaultFromProvider ??
+      (yield* gitCore.resolvePrimaryRemoteName(cwd).pipe(
+        Effect.flatMap((remoteName) => gitCore.resolveDefaultBranchName(cwd, remoteName)),
+        Effect.orElseSucceed(() => null),
+      )) ??
+      "main";
+    return {
+      baseBranch,
+      baseRef: baseBranch,
+      message: missingRemoteBase
+        ? `Base ${missingRemoteBase} is unavailable in this checkout. Using default branch ${baseBranch}.`
+        : null,
+    };
   });
 
   const resolveBaseRangeRef = Effect.fn("resolveBaseRangeRef")(function* (
@@ -2304,16 +2396,19 @@
     const existing = yield* findOpenPr(cwd, headContext);
     if (existing) {
       return {
-        status: "opened_existing" as const,
-        url: existing.url,
-        number: existing.number,
+        pr: {
+          status: "opened_existing" as const,
-        baseBranch: existing.baseRefName,
-        headBranch: existing.headRefName,
-        title: existing.title,
+          url: existing.url,
+          number: existing.number,
+          baseBranch: existing.baseRefName,
+          headBranch: existing.headRefName,
+          title: existing.title,
+        },
+        message: null,
       };
     }
 
-    const { baseBranch, baseRef } = yield* resolvePrBase(
+    const { baseBranch, baseRef, message } = yield* resolvePrBase(
       cwd,
       branch,
       details.upstreamRef,
@@ -2322,7 +2417,7 @@
     yield* emit({
       kind: "phase_started",
       phase: "pr",
-      label: `Generating ${terms.shortLabel} content...`,
+      label: message ?? `Generating ${terms.shortLabel} content...`,
     });
     const baseRangeRef = yield* resolveBaseRangeRef(cwd, baseRef);
     const rangeContext = yield* gitCore.readRangeContext(cwd, baseRangeRef);
@@ -2381,20 +2476,26 @@
     const created = yield* findOpenPr(cwd, headContext);
     if (!created) {
       return {
-        status: "created" as const,
-        baseBranch,
-        headBranch: headContext.headBranch,
+        pr: {
+          status: "created" as const,
+          baseBranch,
+          headBranch: headContext.headBranch,
+          title: generated.title,
+        },
-        title: generated.title,
+        message,
       };
     }
 
     return {
-      status: "created" as const,
-      url: created.url,
-      number: created.number,
-      baseBranch: created.baseRefName,
-      headBranch: created.headRefName,
-      title: created.title,
+      pr: {
+        status: "created" as const,
+        url: created.url,
+        number: created.number,
+        baseBranch: created.baseRefName,
+        headBranch: created.headRefName,
+        title: created.title,
+      },
+      message,
     };
   });
 
@@ -3224,7 +3325,7 @@
               )
           : { status: "skipped_not_requested" as const };
 
-        const pr = wantsPr
+        const { pr, message: prMessage } = wantsPr
           ? yield* progress
               .emit({
                 kind: "phase_started",
@@ -3243,9 +3344,9 @@
                   ),
                 ),
               )
-          : { status: "skipped_not_requested" as const };
+          : { pr: { status: "skipped_not_requested" as const }, message: null };
 
-        const toast = yield* buildCompletionToast(input.cwd, {
+        const completionToast = yield* buildCompletionToast(input.cwd, {
           action: input.action,
           branch: branchStep,
           commit,
@@ -3253,6 +3354,12 @@
           pr,
         });
 
+        const toast = prMessage
+          ? {
+              ...completionToast,
+              description: [completionToast.description, prMessage].filter(Boolean).join(" "),
+            }
+          : completionToast;
         const result = {
           action: input.action,
           branch: branchStep,
```

## knip.jsonc

Status: modified; +1; −0

```diff
@@ -44,6 +44,7 @@
         "scripts/probe-claude-fork-local-rollback-replay.ts",
         "scripts/update-test-shard-weights.ts",
         "scripts/verify-background-live.ts",
+        "scripts/evidence/**/*.mjs",
         "src/provider/testFixtures/*.mjs",
         // graphqlgen reads its config.
         "graphql.config.ts",
```
