fix: surface session branch in frontend (#358)

Co-authored-by: Swyam Sharma <cruzer@Swyams-MacBook-Pro.local>
This commit is contained in:
swyam sharma 2026-06-21 14:57:37 +05:30 committed by GitHub
parent 78cdc6d5ca
commit 34a9fdb736
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 34 additions and 6 deletions

View File

@ -1261,6 +1261,8 @@ components:
properties:
activity:
$ref: '#/components/schemas/DomainActivity'
branch:
type: string
createdAt:
format: date-time
type: string

View File

@ -112,12 +112,14 @@ type CleanupSessionsQuery struct {
}
// SessionView is the session wire shape: the domain read model plus the
// session's attributed pull requests in the curated SessionPRFacts shape. One
// session can own many PRs (e.g. a stack), so prs is a list. The embedded
// domain.Session.PRs field is json:"-"; this curated prs is what serializes.
// display-safe branch name and the session's attributed pull requests in the
// curated SessionPRFacts shape. One session can own many PRs (e.g. a stack), so
// prs is a list. The embedded domain.Session.Metadata and domain.Session.PRs
// fields are json:"-"; these curated fields are what serialize.
type SessionView struct {
domain.Session
PRs []SessionPRFacts `json:"prs"`
Branch string `json:"branch,omitempty"`
PRs []SessionPRFacts `json:"prs"`
}
// ListSessionsResponse is the body of GET /api/v1/sessions.

View File

@ -427,7 +427,7 @@ func writeSessionPRError(w http.ResponseWriter, r *http.Request, err error) {
}
func sessionView(s domain.Session) SessionView {
return SessionView{Session: s, PRs: sessionPRFacts(s.PRs)}
return SessionView{Session: s, Branch: s.Metadata.Branch, PRs: sessionPRFacts(s.PRs)}
}
func sessionViews(sessions []domain.Session) []SessionView {

View File

@ -170,6 +170,9 @@ func TestSessionsRoutes_DefaultToStubsWithoutService(t *testing.T) {
func TestSessionsAPI_ListSpawnGetAndActions(t *testing.T) {
svc := newFakeSessionService()
s := svc.sessions["ao-1"]
s.Metadata = domain.SessionMetadata{Branch: "qa/modal-worker", WorkspacePath: "/tmp/private-worktree", RuntimeHandleID: "runtime-1", Prompt: "private prompt"}
svc.sessions["ao-1"] = s
srv := newSessionTestServer(t, svc)
body, status, _ := doRequest(t, srv, "GET", "/api/v1/sessions?project=ao", "")
@ -183,6 +186,22 @@ func TestSessionsAPI_ListSpawnGetAndActions(t *testing.T) {
if len(list.Sessions) != 1 || list.Sessions[0].ID != "ao-1" || list.Sessions[0].Status != string(domain.StatusIdle) || list.Sessions[0].TerminalHandleID != "ao-1/terminal_0" {
t.Fatalf("list = %#v", list)
}
if list.Sessions[0].Branch != "qa/modal-worker" {
t.Fatalf("branch = %q, want qa/modal-worker", list.Sessions[0].Branch)
}
var rawList struct {
Sessions []map[string]any `json:"sessions"`
}
mustJSON(t, body, &rawList)
if _, ok := rawList.Sessions[0]["metadata"]; ok {
t.Fatalf("list leaked metadata: %s", body)
}
if _, ok := rawList.Sessions[0]["workspacePath"]; ok {
t.Fatalf("list leaked workspacePath: %s", body)
}
if _, ok := rawList.Sessions[0]["prompt"]; ok {
t.Fatalf("list leaked prompt: %s", body)
}
body, status, _ = doRequest(t, srv, "POST", "/api/v1/sessions", `{"projectId":"ao","issueId":"ISS-1","kind":"worker","harness":"codex","prompt":"fix"}`)
if status != http.StatusCreated {
@ -388,6 +407,7 @@ type sessionBody struct {
Kind string `json:"kind"`
Harness string `json:"harness"`
DisplayName string `json:"displayName"`
Branch string `json:"branch"`
Status string `json:"status"`
TerminalHandleID string `json:"terminalHandleId"`
}

View File

@ -446,6 +446,7 @@ export interface components {
};
ControllersSessionView: {
activity: components["schemas"]["DomainActivity"];
branch?: string;
/** Format: date-time */
createdAt: string;
displayName?: string;

View File

@ -63,6 +63,7 @@ describe("useWorkspaceQuery", () => {
terminalHandleId: "term-1",
displayName: "fix-bug",
harness: "claude-code",
branch: "qa/modal-worker",
status: "mergeable",
isTerminated: false,
updatedAt: "2026-06-10T16:15:04Z",
@ -96,12 +97,14 @@ describe("useWorkspaceQuery", () => {
terminalHandleId: "term-1",
title: "fix-bug",
provider: "claude-code",
branch: "qa/modal-worker",
status: "mergeable",
});
expect(workspace.sessions[1]).toMatchObject({
id: "sess-2",
title: "sess-2",
provider: "codex",
branch: "session/sess-2",
status: "working",
});
});

View File

@ -53,7 +53,7 @@ async function fetchWorkspaces(): Promise<WorkspaceSummary[]> {
title: session.displayName ?? session.issueId ?? session.id,
provider: toAgentProvider(session.harness),
kind: session.kind === "orchestrator" ? "orchestrator" : session.kind === "worker" ? "worker" : undefined,
branch: `session/${session.id}`,
branch: session.branch ?? `session/${session.id}`,
status: toSessionStatus(session.status, session.isTerminated),
createdAt: session.createdAt,
updatedAt: session.updatedAt,