authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-28 11:10:18-08:00
committergravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-28 11:10:18-08:00
log7f2a95fe14c18b3feba039df6b88c6c47a846d04
treef9413d88322916a8456614e8e89e07d52ea2b435
parent4b3825e500594250cc9aeec9f6182a38059085f1

Prefer open PR when resolving git status PR

- Update GitManager PR selection to return the latest open PR before merged ones - Add regression test covering open-vs-merged ordering by updatedAt

2 files changed, 52 insertions(+), 0 deletions(-)

apps/server/src/git/Layers/GitManager.test.ts+47
...@@ -373,6 +373,53 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {...@@ -373,6 +373,53 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => {
373 }),373 }),
374 );374 );
375375
376 it.effect("status prefers open PR when merged PR has newer updatedAt", () =>
377 Effect.gen(function* () {
378 const repoDir = yield* makeTempDir("t3code-git-manager-");
379 yield* initRepo(repoDir);
380 yield* runGit(repoDir, ["checkout", "-b", "feature/status-open-over-merged"]);
381
382 const { manager } = yield* makeManager({
383 ghScenario: {
384 prListSequence: [
385 JSON.stringify([
386 {
387 number: 45,
388 title: "Merged PR",
389 url: "https://github.com/pingdotgg/codething-mvp/pull/45",
390 baseRefName: "main",
391 headRefName: "feature/status-open-over-merged",
392 state: "MERGED",
393 mergedAt: "2026-01-31T10:00:00Z",
394 updatedAt: "2026-02-01T10:00:00Z",
395 },
396 {
397 number: 46,
398 title: "Open PR",
399 url: "https://github.com/pingdotgg/codething-mvp/pull/46",
400 baseRefName: "main",
401 headRefName: "feature/status-open-over-merged",
402 state: "OPEN",
403 updatedAt: "2026-01-30T10:00:00Z",
404 },
405 ]),
406 ],
407 },
408 });
409
410 const status = yield* manager.status({ cwd: repoDir });
411 expect(status.branch).toBe("feature/status-open-over-merged");
412 expect(status.pr).toEqual({
413 number: 46,
414 title: "Open PR",
415 url: "https://github.com/pingdotgg/codething-mvp/pull/46",
416 baseBranch: "main",
417 headBranch: "feature/status-open-over-merged",
418 state: "open",
419 });
420 }),
421 );
422
376 it.effect("status is resilient to gh lookup failures and returns pr null", () =>423 it.effect("status is resilient to gh lookup failures and returns pr null", () =>
377 Effect.gen(function* () {424 Effect.gen(function* () {
378 const repoDir = yield* makeTempDir("t3code-git-manager-");425 const repoDir = yield* makeTempDir("t3code-git-manager-");
apps/server/src/git/Layers/GitManager.ts+5
...@@ -223,6 +223,11 @@ export const makeGitManager = Effect.gen(function* () {...@@ -223,6 +223,11 @@ export const makeGitManager = Effect.gen(function* () {
223 const right = b.updatedAt ? Date.parse(b.updatedAt) : 0;223 const right = b.updatedAt ? Date.parse(b.updatedAt) : 0;
224 return right - left;224 return right - left;
225 });225 });
226
227 const latestOpenPr = parsed.find((pr) => pr.state === "open");
228 if (latestOpenPr) {
229 return latestOpenPr;
230 }
226 return parsed[0] ?? null;231 return parsed[0] ?? null;
227 });232 });
228233