Rename Foundry handoffs to tasks (#239)

* Restore foundry onboarding stack

* Consolidate foundry rename

* Create foundry tasks without prompts

* Rename Foundry handoffs to tasks
This commit is contained in:
Nathan Flurry 2026-03-11 13:23:54 -07:00 committed by GitHub
parent d30cc0bcc8
commit d75e8c31d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
281 changed files with 9242 additions and 4356 deletions

View file

@ -0,0 +1,84 @@
import { describe, expect, it } from "vitest";
import type { TaskRecord } from "@sandbox-agent/foundry-shared";
import { formatDiffStat, groupTasksByRepo } from "./model";
const base: TaskRecord = {
workspaceId: "default",
repoId: "repo-a",
repoRemote: "https://example.com/repo-a.git",
taskId: "task-1",
branchName: "feature/one",
title: "Feature one",
task: "Ship one",
providerId: "daytona",
status: "running",
statusMessage: null,
activeSandboxId: "sandbox-1",
activeSessionId: "session-1",
sandboxes: [
{
sandboxId: "sandbox-1",
providerId: "daytona",
sandboxActorId: null,
switchTarget: "daytona://sandbox-1",
cwd: null,
createdAt: 10,
updatedAt: 10,
},
],
agentType: null,
prSubmitted: false,
diffStat: null,
prUrl: null,
prAuthor: null,
ciStatus: null,
reviewStatus: null,
reviewer: null,
conflictsWithMain: null,
hasUnpushed: null,
parentBranch: null,
createdAt: 10,
updatedAt: 10,
};
describe("groupTasksByRepo", () => {
it("groups by repo and sorts by recency", () => {
const rows: TaskRecord[] = [
{ ...base, taskId: "h1", repoId: "repo-a", repoRemote: "https://example.com/repo-a.git", updatedAt: 10 },
{ ...base, taskId: "h2", repoId: "repo-a", repoRemote: "https://example.com/repo-a.git", updatedAt: 50 },
{ ...base, taskId: "h3", repoId: "repo-b", repoRemote: "https://example.com/repo-b.git", updatedAt: 30 },
];
const groups = groupTasksByRepo(rows);
expect(groups).toHaveLength(2);
expect(groups[0]?.repoId).toBe("repo-a");
expect(groups[0]?.tasks[0]?.taskId).toBe("h2");
});
it("sorts repo groups by latest task activity first", () => {
const rows: TaskRecord[] = [
{ ...base, taskId: "h1", repoId: "repo-z", repoRemote: "https://example.com/repo-z.git", updatedAt: 200 },
{ ...base, taskId: "h2", repoId: "repo-a", repoRemote: "https://example.com/repo-a.git", updatedAt: 100 },
];
const groups = groupTasksByRepo(rows);
expect(groups[0]?.repoId).toBe("repo-z");
expect(groups[1]?.repoId).toBe("repo-a");
});
});
describe("formatDiffStat", () => {
it("returns No changes for zero-diff values", () => {
expect(formatDiffStat("+0/-0")).toBe("No changes");
expect(formatDiffStat("+0 -0")).toBe("No changes");
});
it("returns dash for empty values", () => {
expect(formatDiffStat(null)).toBe("-");
expect(formatDiffStat("")).toBe("-");
});
it("keeps non-empty non-zero diff stats", () => {
expect(formatDiffStat("+12/-4")).toBe("+12/-4");
});
});

View file

@ -0,0 +1,50 @@
import type { TaskRecord } from "@sandbox-agent/foundry-shared";
export interface RepoGroup {
repoId: string;
repoRemote: string;
tasks: TaskRecord[];
}
export function groupTasksByRepo(tasks: TaskRecord[]): RepoGroup[] {
const groups = new Map<string, RepoGroup>();
for (const task of tasks) {
const group = groups.get(task.repoId);
if (group) {
group.tasks.push(task);
continue;
}
groups.set(task.repoId, {
repoId: task.repoId,
repoRemote: task.repoRemote,
tasks: [task],
});
}
return Array.from(groups.values())
.map((group) => ({
...group,
tasks: [...group.tasks].sort((a, b) => b.updatedAt - a.updatedAt),
}))
.sort((a, b) => {
const aLatest = a.tasks[0]?.updatedAt ?? 0;
const bLatest = b.tasks[0]?.updatedAt ?? 0;
if (aLatest !== bLatest) {
return bLatest - aLatest;
}
return a.repoRemote.localeCompare(b.repoRemote);
});
}
export function formatDiffStat(diffStat: string | null | undefined): string {
const normalized = diffStat?.trim();
if (!normalized) {
return "-";
}
if (normalized === "+0/-0" || normalized === "+0 -0" || normalized === "0 files changed") {
return "No changes";
}
return normalized;
}