Fix Foundry validation fallout

This commit is contained in:
Nathan Flurry 2026-03-15 13:43:24 -07:00 committed by Nathan Flurry
parent 13fc9cb318
commit aa332307e5
11 changed files with 25 additions and 7 deletions

View file

@ -138,6 +138,7 @@ function pullRequestSummaryFromRow(row: any) {
repoId: row.repoId,
repoFullName: row.repoFullName,
number: row.number,
status: Boolean(row.isDraft) ? "draft" : "ready",
title: row.title,
state: row.state,
url: row.url,

View file

@ -1089,6 +1089,7 @@ export const organizationAppActions = {
},
pullRequest: {
number: body.pull_request.number,
status: body.pull_request.draft ? "draft" : "ready",
title: body.pull_request.title ?? "",
body: body.pull_request.body ?? null,
state: body.pull_request.state ?? "open",

View file

@ -1378,6 +1378,7 @@ export async function publishWorkspacePr(c: any): Promise<void> {
});
await syncTaskPullRequest(c, {
number: created.number,
status: "ready",
title: record.title ?? record.task,
body: null,
state: "open",

View file

@ -7,6 +7,7 @@ import type {
CreateTaskInput,
AppEvent,
SessionEvent,
SandboxProcessSnapshot,
SandboxProcessesEvent,
TaskRecord,
TaskSummary,
@ -40,7 +41,7 @@ import type {
WorkspaceModelGroup,
WorkspaceModelId,
} from "@sandbox-agent/foundry-shared";
import type { ProcessCreateRequest, ProcessInfo, ProcessLogFollowQuery, ProcessLogsResponse, ProcessSignalQuery } from "sandbox-agent";
import type { ProcessCreateRequest, ProcessLogFollowQuery, ProcessLogsResponse, ProcessSignalQuery } from "sandbox-agent";
import { createMockBackendClient } from "./mock/backend-client.js";
import { taskKey, taskSandboxKey, organizationKey } from "./keys.js";
@ -66,7 +67,7 @@ export interface SandboxSessionEventRecord {
payload: unknown;
}
export type SandboxProcessRecord = ProcessInfo;
export type SandboxProcessRecord = SandboxProcessSnapshot;
export interface ActorConn {
on(event: string, listener: (payload: any) => void): () => void;

View file

@ -142,6 +142,7 @@ class MockWorkspaceStore implements TaskWorkspaceClient {
updatedAtMs: nowMs(),
pullRequest: {
number: nextPrNumber,
status: "ready",
title: task.title,
state: "open",
url: `https://example.test/pr/${nextPrNumber}`,

View file

@ -198,6 +198,7 @@ function buildPullRequestSummary(params: {
}) {
return {
number: params.number,
status: params.status,
title: params.title,
state: "open",
url: `https://github.com/${params.repoName}/pull/${params.number}`,

View file

@ -132,11 +132,11 @@ describe("e2e(client): full integration stack workflow", () => {
90_000,
1_000,
async () => client.getRepoOverview(organizationId, repo.repoId),
(value) => value.branches.some((row) => row.branchName === seededBranch),
(value) => value.branches.some((row: RepoOverview["branches"][number]) => row.branchName === seededBranch),
);
const postActionOverview = await client.getRepoOverview(organizationId, repo.repoId);
const seededRow = postActionOverview.branches.find((row) => row.branchName === seededBranch);
const seededRow = postActionOverview.branches.find((row: RepoOverview["branches"][number]) => row.branchName === seededBranch);
expect(Boolean(seededRow)).toBe(true);
expect(postActionOverview.fetchedAt).toBeGreaterThanOrEqual(overview.fetchedAt);
} finally {

View file

@ -176,27 +176,32 @@ describe("e2e(client): workspace flows", () => {
expect(transcriptIncludesAgentText(findTab(initialCompleted, primaryTab.id).transcript, expectedInitialReply)).toBe(true);
await client.renameWorkspaceTask(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
value: `Workspace E2E ${runId} Renamed`,
});
await client.renameWorkspaceSession(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: primaryTab.id,
title: "Primary Session",
});
const secondTab = await client.createWorkspaceSession(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
model,
});
await client.renameWorkspaceSession(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: secondTab.sessionId,
title: "Follow-up Session",
});
await client.updateWorkspaceDraft(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: secondTab.sessionId,
text: [
@ -219,6 +224,7 @@ describe("e2e(client): workspace flows", () => {
expect(findTab(drafted, secondTab.sessionId).draft.attachments).toHaveLength(1);
await client.sendWorkspaceMessage(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: secondTab.sessionId,
text: [
@ -254,16 +260,18 @@ describe("e2e(client): workspace flows", () => {
expect(withSecondReply.fileChanges.some((file) => file.path === expectedFile)).toBe(true);
await client.setWorkspaceSessionUnread(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: secondTab.sessionId,
unread: false,
});
await client.markWorkspaceUnread(organizationId, { taskId: created.taskId });
await client.markWorkspaceUnread(organizationId, { repoId: repo.repoId, taskId: created.taskId });
const unreadSnapshot = findTask(await client.getWorkspace(organizationId), created.taskId);
expect(unreadSnapshot.sessions.some((tab) => tab.unread)).toBe(true);
await client.closeWorkspaceSession(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: secondTab.sessionId,
});
@ -278,6 +286,7 @@ describe("e2e(client): workspace flows", () => {
expect(closedSnapshot.sessions).toHaveLength(1);
await client.revertWorkspaceFile(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
path: expectedFile,
});

View file

@ -245,12 +245,14 @@ describe("e2e(client): workspace load", () => {
const expectedReply = `LOAD_REPLY_${runId}_${sessionIndex}`;
const createSessionStartedAt = performance.now();
const createdSession = await client.createWorkspaceSession(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
model,
});
createSessionLatencies.push(performance.now() - createSessionStartedAt);
await client.sendWorkspaceMessage(organizationId, {
repoId: repo.repoId,
taskId: created.taskId,
sessionId: createdSession.sessionId,
text: `Run pwd in the repo, then reply with exactly: ${expectedReply}`,

View file

@ -60,6 +60,7 @@ export type CreateTaskInput = z.infer<typeof CreateTaskInputSchema>;
export const WorkspacePullRequestSummarySchema = z.object({
number: z.number().int(),
status: z.enum(["draft", "ready"]),
title: z.string().min(1),
state: z.string().min(1),
url: z.string().min(1),

View file

@ -1,4 +1,4 @@
import { pino, type Logger, type LoggerOptions } from "pino";
import { pino, type LogFn, type Logger, type LoggerOptions } from "pino";
export interface FoundryLoggerOptions {
service: string;
@ -160,7 +160,7 @@ export function createFoundryLogger(options: FoundryLoggerOptions): Logger {
loggerOptions.timestamp = pino.stdTimeFunctions.isoTime;
if (options.format === "logfmt") {
loggerOptions.hooks = {
logMethod(this: Logger, args, _method, level) {
logMethod(this: Logger, args: Parameters<LogFn>, _method: LogFn, level: number) {
const levelLabel = this.levels.labels[level] ?? "info";
const record = buildLogRecord(levelLabel, this.bindings(), args);
writeLogfmtLine(formatLogfmtLine(record));