refactor(server): extract checkout status projection (#838)

This commit is contained in:
Mohamed Boudra
2026-05-09 21:28:15 +08:00
committed by GitHub
parent d5ffc51f07
commit e5deb90986
4 changed files with 183 additions and 178 deletions
@@ -0,0 +1,38 @@
import { describe, expect, test } from "vitest";
import { CheckoutPrStatusSchema } from "../../shared/messages.js";
import { normalizeCheckoutPrStatusPayload } from "./status-projection.js";
describe("checkout status projection", () => {
test("includes repository identity fields on the PR status wire payload", () => {
const payload = normalizeCheckoutPrStatusPayload({
number: 123,
repoOwner: "internal-owner",
repoName: "internal-repo",
url: "https://github.com/getpaseo/paseo/pull/123",
title: "Ship PR pane",
state: "open",
baseRefName: "main",
headRefName: "feature/pr-pane",
isMerged: false,
isDraft: true,
mergeable: "MERGEABLE",
checks: [
{
name: "typecheck",
status: "success",
url: "https://github.com/getpaseo/paseo/actions/runs/1",
workflow: "CI",
duration: "1m 20s",
},
],
checksStatus: "success",
reviewDecision: "approved",
});
expect(payload).toHaveProperty("repoOwner", "internal-owner");
expect(payload).toHaveProperty("repoName", "internal-repo");
expect(payload).toHaveProperty("mergeable", "MERGEABLE");
expect(CheckoutPrStatusSchema.parse(payload)).toEqual(payload);
});
});
@@ -0,0 +1,134 @@
import type {
CheckoutPrStatusResponse,
CheckoutStatusResponse,
SessionOutboundMessage,
} from "../../shared/messages.js";
import type { WorkspaceGitRuntimeSnapshot } from "../workspace-git-service.js";
type CheckoutPrStatusPayload = Extract<
SessionOutboundMessage,
{ type: "checkout_pr_status_response" }
>["payload"];
type CheckoutPrStatusPayloadStatus = NonNullable<CheckoutPrStatusPayload["status"]>;
export function buildCheckoutStatusPayloadFromSnapshot({
cwd,
requestId,
snapshot,
}: {
cwd: string;
requestId: string;
snapshot: WorkspaceGitRuntimeSnapshot;
}): CheckoutStatusResponse["payload"] {
if (!snapshot.git.isGit) {
return {
cwd,
isGit: false,
repoRoot: null,
currentBranch: null,
isDirty: null,
baseRef: null,
aheadBehind: null,
aheadOfOrigin: null,
behindOfOrigin: null,
hasRemote: false,
remoteUrl: null,
isPaseoOwnedWorktree: false,
error: null,
requestId,
};
}
if (snapshot.git.repoRoot === null || snapshot.git.isDirty === null) {
throw new Error("Workspace git snapshot is missing required checkout status fields");
}
if (snapshot.git.isPaseoOwnedWorktree) {
if (snapshot.git.mainRepoRoot === null || snapshot.git.baseRef === null) {
throw new Error("Workspace git snapshot is missing required worktree status fields");
}
return {
cwd,
isGit: true,
repoRoot: snapshot.git.repoRoot,
mainRepoRoot: snapshot.git.mainRepoRoot,
currentBranch: snapshot.git.currentBranch ?? null,
isDirty: snapshot.git.isDirty,
baseRef: snapshot.git.baseRef,
aheadBehind: snapshot.git.aheadBehind ?? null,
aheadOfOrigin: snapshot.git.aheadOfOrigin ?? null,
behindOfOrigin: snapshot.git.behindOfOrigin ?? null,
hasRemote: snapshot.git.hasRemote,
remoteUrl: snapshot.git.remoteUrl,
isPaseoOwnedWorktree: true,
error: null,
requestId,
};
}
return {
cwd,
isGit: true,
repoRoot: snapshot.git.repoRoot,
mainRepoRoot: snapshot.git.mainRepoRoot,
currentBranch: snapshot.git.currentBranch ?? null,
isDirty: snapshot.git.isDirty,
baseRef: snapshot.git.baseRef ?? null,
aheadBehind: snapshot.git.aheadBehind ?? null,
aheadOfOrigin: snapshot.git.aheadOfOrigin ?? null,
behindOfOrigin: snapshot.git.behindOfOrigin ?? null,
hasRemote: snapshot.git.hasRemote,
remoteUrl: snapshot.git.remoteUrl,
isPaseoOwnedWorktree: false,
error: null,
requestId,
};
}
export function buildCheckoutPrStatusPayloadFromSnapshot({
cwd,
requestId,
snapshot,
}: {
cwd: string;
requestId: string;
snapshot: WorkspaceGitRuntimeSnapshot;
}): CheckoutPrStatusResponse["payload"] {
return {
cwd,
status: normalizeCheckoutPrStatusPayload(snapshot.github.pullRequest),
githubFeaturesEnabled: snapshot.github.featuresEnabled,
error: snapshot.github.error
? {
code: "UNKNOWN",
message: snapshot.github.error.message,
}
: null,
requestId,
};
}
export function normalizeCheckoutPrStatusPayload(
status: WorkspaceGitRuntimeSnapshot["github"]["pullRequest"],
): CheckoutPrStatusPayloadStatus | null {
if (!status) {
return null;
}
return {
number: status.number,
url: status.url,
title: status.title,
state: status.state,
repoOwner: status.repoOwner,
repoName: status.repoName,
baseRefName: status.baseRefName,
headRefName: status.headRefName,
isMerged: status.isMerged,
isDraft: status.isDraft ?? false,
mergeable: status.mergeable ?? "UNKNOWN",
checks: status.checks ?? [],
checksStatus: status.checksStatus,
reviewDecision: status.reviewDecision,
};
}
+1 -36
View File
@@ -6,10 +6,9 @@ import { join } from "path";
import pino from "pino"; import pino from "pino";
import { afterEach, describe, expect, test, vi } from "vitest"; import { afterEach, describe, expect, test, vi } from "vitest";
import { CheckoutPrStatusSchema } from "../shared/messages.js";
import type { WorkspaceDescriptorPayload } from "../shared/messages.js"; import type { WorkspaceDescriptorPayload } from "../shared/messages.js";
import { decodeFileTransferFrame, FileTransferOpcode } from "../shared/binary-frames/index.js"; import { decodeFileTransferFrame, FileTransferOpcode } from "../shared/binary-frames/index.js";
import { normalizeCheckoutPrStatusPayload, Session } from "./session.js"; import { Session } from "./session.js";
import { StructuredAgentFallbackError } from "./agent/agent-response-loop.js"; import { StructuredAgentFallbackError } from "./agent/agent-response-loop.js";
import type { import type {
AgentClient, AgentClient,
@@ -1191,40 +1190,6 @@ describe("session agent import", () => {
}); });
}); });
describe("session PR status payload normalization", () => {
test("includes repository identity fields on the wire", () => {
const payload = normalizeCheckoutPrStatusPayload({
number: 123,
repoOwner: "internal-owner",
repoName: "internal-repo",
url: "https://github.com/getpaseo/paseo/pull/123",
title: "Ship PR pane",
state: "open",
baseRefName: "main",
headRefName: "feature/pr-pane",
isMerged: false,
isDraft: true,
mergeable: "MERGEABLE",
checks: [
{
name: "typecheck",
status: "success",
url: "https://github.com/getpaseo/paseo/actions/runs/1",
workflow: "CI",
duration: "1m 20s",
},
],
checksStatus: "success",
reviewDecision: "approved",
});
expect(payload).toHaveProperty("repoOwner", "internal-owner");
expect(payload).toHaveProperty("repoName", "internal-repo");
expect(payload).toHaveProperty("mergeable", "MERGEABLE");
expect(CheckoutPrStatusSchema.parse(payload)).toEqual(payload);
});
});
describe("session provider refresh cwd routing", () => { describe("session provider refresh cwd routing", () => {
test("routes no-cwd provider snapshot refreshes through settings refresh", async () => { test("routes no-cwd provider snapshot refreshes through settings refresh", async () => {
const { const {
+10 -142
View File
@@ -20,8 +20,6 @@ import {
type FileExplorerRequest, type FileExplorerRequest,
type FileDownloadTokenRequest, type FileDownloadTokenRequest,
type GitSetupOptions, type GitSetupOptions,
type CheckoutPrStatusResponse,
type CheckoutStatusResponse,
type StartWorkspaceScriptRequest, type StartWorkspaceScriptRequest,
type CloseItemsRequest, type CloseItemsRequest,
type SubscribeCheckoutDiffRequest, type SubscribeCheckoutDiffRequest,
@@ -192,6 +190,10 @@ import { expandTilde } from "../utils/path.js";
import { searchHomeDirectories, searchWorkspaceEntries } from "../utils/directory-suggestions.js"; import { searchHomeDirectories, searchWorkspaceEntries } from "../utils/directory-suggestions.js";
import { toCheckoutError } from "./checkout-git-utils.js"; import { toCheckoutError } from "./checkout-git-utils.js";
import { CheckoutDiffManager } from "./checkout-diff-manager.js"; import { CheckoutDiffManager } from "./checkout-diff-manager.js";
import {
buildCheckoutPrStatusPayloadFromSnapshot,
buildCheckoutStatusPayloadFromSnapshot,
} from "./checkout/status-projection.js";
import type { LocalSpeechModelId } from "./speech/providers/local/models.js"; import type { LocalSpeechModelId } from "./speech/providers/local/models.js";
import { toResolver, type Resolvable } from "./speech/provider-resolver.js"; import { toResolver, type Resolvable } from "./speech/provider-resolver.js";
import type { SpeechReadinessSnapshot, SpeechReadinessState } from "./speech/speech-runtime.js"; import type { SpeechReadinessSnapshot, SpeechReadinessState } from "./speech/speech-runtime.js";
@@ -636,11 +638,6 @@ export type SessionLifecycleIntent =
reason?: string; reason?: string;
}; };
type CheckoutPrStatusPayload = Extract<
SessionOutboundMessage,
{ type: "checkout_pr_status_response" }
>["payload"];
type CheckoutPrStatusPayloadStatus = NonNullable<CheckoutPrStatusPayload["status"]>;
type PullRequestTimelinePayload = Extract< type PullRequestTimelinePayload = Extract<
SessionOutboundMessage, SessionOutboundMessage,
{ type: "pull_request_timeline_response" } { type: "pull_request_timeline_response" }
@@ -4489,7 +4486,7 @@ export class Session {
const snapshot = await this.workspaceGitService.getSnapshot(resolvedCwd); const snapshot = await this.workspaceGitService.getSnapshot(resolvedCwd);
this.emit({ this.emit({
type: "checkout_status_response", type: "checkout_status_response",
payload: this.buildCheckoutStatusPayloadFromSnapshot({ payload: buildCheckoutStatusPayloadFromSnapshot({
cwd, cwd,
requestId, requestId,
snapshot, snapshot,
@@ -4838,116 +4835,18 @@ export class Session {
this.checkoutDiffSubscriptions.delete(msg.subscriptionId); this.checkoutDiffSubscriptions.delete(msg.subscriptionId);
} }
private buildCheckoutStatusPayloadFromSnapshot({
cwd,
requestId,
snapshot,
}: {
cwd: string;
requestId: string;
snapshot: WorkspaceGitRuntimeSnapshot;
}): CheckoutStatusResponse["payload"] {
if (!snapshot.git.isGit) {
return {
cwd,
isGit: false,
repoRoot: null,
currentBranch: null,
isDirty: null,
baseRef: null,
aheadBehind: null,
aheadOfOrigin: null,
behindOfOrigin: null,
hasRemote: false,
remoteUrl: null,
isPaseoOwnedWorktree: false,
error: null,
requestId,
};
}
if (snapshot.git.repoRoot === null || snapshot.git.isDirty === null) {
throw new Error("Workspace git snapshot is missing required checkout status fields");
}
if (snapshot.git.isPaseoOwnedWorktree) {
if (snapshot.git.mainRepoRoot === null || snapshot.git.baseRef === null) {
throw new Error("Workspace git snapshot is missing required worktree status fields");
}
return {
cwd,
isGit: true,
repoRoot: snapshot.git.repoRoot,
mainRepoRoot: snapshot.git.mainRepoRoot,
currentBranch: snapshot.git.currentBranch ?? null,
isDirty: snapshot.git.isDirty,
baseRef: snapshot.git.baseRef,
aheadBehind: snapshot.git.aheadBehind ?? null,
aheadOfOrigin: snapshot.git.aheadOfOrigin ?? null,
behindOfOrigin: snapshot.git.behindOfOrigin ?? null,
hasRemote: snapshot.git.hasRemote,
remoteUrl: snapshot.git.remoteUrl,
isPaseoOwnedWorktree: true,
error: null,
requestId,
};
}
return {
cwd,
isGit: true,
repoRoot: snapshot.git.repoRoot,
mainRepoRoot: snapshot.git.mainRepoRoot,
currentBranch: snapshot.git.currentBranch ?? null,
isDirty: snapshot.git.isDirty,
baseRef: snapshot.git.baseRef ?? null,
aheadBehind: snapshot.git.aheadBehind ?? null,
aheadOfOrigin: snapshot.git.aheadOfOrigin ?? null,
behindOfOrigin: snapshot.git.behindOfOrigin ?? null,
hasRemote: snapshot.git.hasRemote,
remoteUrl: snapshot.git.remoteUrl,
isPaseoOwnedWorktree: false,
error: null,
requestId,
};
}
private buildCheckoutPrStatusPayloadFromSnapshot({
cwd,
requestId,
snapshot,
}: {
cwd: string;
requestId: string;
snapshot: WorkspaceGitRuntimeSnapshot;
}): CheckoutPrStatusResponse["payload"] {
return {
cwd,
status: normalizeCheckoutPrStatusPayload(snapshot.github.pullRequest),
githubFeaturesEnabled: snapshot.github.featuresEnabled,
error: snapshot.github.error
? {
code: "UNKNOWN",
message: snapshot.github.error.message,
}
: null,
requestId,
};
}
private emitCheckoutStatusUpdate(cwd: string, snapshot: WorkspaceGitRuntimeSnapshot): void { private emitCheckoutStatusUpdate(cwd: string, snapshot: WorkspaceGitRuntimeSnapshot): void {
try { try {
const requestId = `subscription:${cwd}`; const requestId = `subscription:${cwd}`;
this.emit({ this.emit({
type: "checkout_status_update", type: "checkout_status_update",
payload: { payload: {
...this.buildCheckoutStatusPayloadFromSnapshot({ ...buildCheckoutStatusPayloadFromSnapshot({
cwd, cwd,
requestId, requestId,
snapshot, snapshot,
}), }),
prStatus: this.buildCheckoutPrStatusPayloadFromSnapshot({ prStatus: buildCheckoutPrStatusPayloadFromSnapshot({
cwd, cwd,
requestId, requestId,
snapshot, snapshot,
@@ -5382,18 +5281,11 @@ export class Session {
const snapshot = await this.workspaceGitService.getSnapshot(cwd); const snapshot = await this.workspaceGitService.getSnapshot(cwd);
this.emit({ this.emit({
type: "checkout_pr_status_response", type: "checkout_pr_status_response",
payload: { payload: buildCheckoutPrStatusPayloadFromSnapshot({
cwd, cwd,
status: normalizeCheckoutPrStatusPayload(snapshot.github.pullRequest),
githubFeaturesEnabled: snapshot.github.featuresEnabled,
error: snapshot.github.error
? {
code: "UNKNOWN",
message: snapshot.github.error.message,
}
: null,
requestId, requestId,
}, snapshot,
}),
}); });
} catch (error) { } catch (error) {
this.emit({ this.emit({
@@ -8895,30 +8787,6 @@ export class Session {
} }
} }
export function normalizeCheckoutPrStatusPayload(
status: WorkspaceGitRuntimeSnapshot["github"]["pullRequest"],
): CheckoutPrStatusPayloadStatus | null {
if (!status) {
return null;
}
return {
number: status.number,
url: status.url,
title: status.title,
state: status.state,
repoOwner: status.repoOwner,
repoName: status.repoName,
baseRefName: status.baseRefName,
headRefName: status.headRefName,
isMerged: status.isMerged,
isDraft: status.isDraft ?? false,
mergeable: status.mergeable ?? "UNKNOWN",
checks: status.checks ?? [],
checksStatus: status.checksStatus,
reviewDecision: status.reviewDecision,
};
}
function isValidPullRequestTimelineIdentity(options: { function isValidPullRequestTimelineIdentity(options: {
prNumber: number; prNumber: number;
repoOwner: string; repoOwner: string;