fix: surface session branch in frontend (#358)
Co-authored-by: Swyam Sharma <cruzer@Swyams-MacBook-Pro.local>
This commit is contained in:
parent
78cdc6d5ca
commit
34a9fdb736
|
|
@ -1261,6 +1261,8 @@ components:
|
||||||
properties:
|
properties:
|
||||||
activity:
|
activity:
|
||||||
$ref: '#/components/schemas/DomainActivity'
|
$ref: '#/components/schemas/DomainActivity'
|
||||||
|
branch:
|
||||||
|
type: string
|
||||||
createdAt:
|
createdAt:
|
||||||
format: date-time
|
format: date-time
|
||||||
type: string
|
type: string
|
||||||
|
|
|
||||||
|
|
@ -112,12 +112,14 @@ type CleanupSessionsQuery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionView is the session wire shape: the domain read model plus the
|
// SessionView is the session wire shape: the domain read model plus the
|
||||||
// session's attributed pull requests in the curated SessionPRFacts shape. One
|
// display-safe branch name and the session's attributed pull requests in the
|
||||||
// session can own many PRs (e.g. a stack), so prs is a list. The embedded
|
// curated SessionPRFacts shape. One session can own many PRs (e.g. a stack), so
|
||||||
// domain.Session.PRs field is json:"-"; this curated prs is what serializes.
|
// 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 {
|
type SessionView struct {
|
||||||
domain.Session
|
domain.Session
|
||||||
PRs []SessionPRFacts `json:"prs"`
|
Branch string `json:"branch,omitempty"`
|
||||||
|
PRs []SessionPRFacts `json:"prs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListSessionsResponse is the body of GET /api/v1/sessions.
|
// ListSessionsResponse is the body of GET /api/v1/sessions.
|
||||||
|
|
|
||||||
|
|
@ -427,7 +427,7 @@ func writeSessionPRError(w http.ResponseWriter, r *http.Request, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sessionView(s domain.Session) SessionView {
|
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 {
|
func sessionViews(sessions []domain.Session) []SessionView {
|
||||||
|
|
|
||||||
|
|
@ -170,6 +170,9 @@ func TestSessionsRoutes_DefaultToStubsWithoutService(t *testing.T) {
|
||||||
|
|
||||||
func TestSessionsAPI_ListSpawnGetAndActions(t *testing.T) {
|
func TestSessionsAPI_ListSpawnGetAndActions(t *testing.T) {
|
||||||
svc := newFakeSessionService()
|
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)
|
srv := newSessionTestServer(t, svc)
|
||||||
|
|
||||||
body, status, _ := doRequest(t, srv, "GET", "/api/v1/sessions?project=ao", "")
|
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" {
|
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)
|
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"}`)
|
body, status, _ = doRequest(t, srv, "POST", "/api/v1/sessions", `{"projectId":"ao","issueId":"ISS-1","kind":"worker","harness":"codex","prompt":"fix"}`)
|
||||||
if status != http.StatusCreated {
|
if status != http.StatusCreated {
|
||||||
|
|
@ -388,6 +407,7 @@ type sessionBody struct {
|
||||||
Kind string `json:"kind"`
|
Kind string `json:"kind"`
|
||||||
Harness string `json:"harness"`
|
Harness string `json:"harness"`
|
||||||
DisplayName string `json:"displayName"`
|
DisplayName string `json:"displayName"`
|
||||||
|
Branch string `json:"branch"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
TerminalHandleID string `json:"terminalHandleId"`
|
TerminalHandleID string `json:"terminalHandleId"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -446,6 +446,7 @@ export interface components {
|
||||||
};
|
};
|
||||||
ControllersSessionView: {
|
ControllersSessionView: {
|
||||||
activity: components["schemas"]["DomainActivity"];
|
activity: components["schemas"]["DomainActivity"];
|
||||||
|
branch?: string;
|
||||||
/** Format: date-time */
|
/** Format: date-time */
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
displayName?: string;
|
displayName?: string;
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ describe("useWorkspaceQuery", () => {
|
||||||
terminalHandleId: "term-1",
|
terminalHandleId: "term-1",
|
||||||
displayName: "fix-bug",
|
displayName: "fix-bug",
|
||||||
harness: "claude-code",
|
harness: "claude-code",
|
||||||
|
branch: "qa/modal-worker",
|
||||||
status: "mergeable",
|
status: "mergeable",
|
||||||
isTerminated: false,
|
isTerminated: false,
|
||||||
updatedAt: "2026-06-10T16:15:04Z",
|
updatedAt: "2026-06-10T16:15:04Z",
|
||||||
|
|
@ -96,12 +97,14 @@ describe("useWorkspaceQuery", () => {
|
||||||
terminalHandleId: "term-1",
|
terminalHandleId: "term-1",
|
||||||
title: "fix-bug",
|
title: "fix-bug",
|
||||||
provider: "claude-code",
|
provider: "claude-code",
|
||||||
|
branch: "qa/modal-worker",
|
||||||
status: "mergeable",
|
status: "mergeable",
|
||||||
});
|
});
|
||||||
expect(workspace.sessions[1]).toMatchObject({
|
expect(workspace.sessions[1]).toMatchObject({
|
||||||
id: "sess-2",
|
id: "sess-2",
|
||||||
title: "sess-2",
|
title: "sess-2",
|
||||||
provider: "codex",
|
provider: "codex",
|
||||||
|
branch: "session/sess-2",
|
||||||
status: "working",
|
status: "working",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ async function fetchWorkspaces(): Promise<WorkspaceSummary[]> {
|
||||||
title: session.displayName ?? session.issueId ?? session.id,
|
title: session.displayName ?? session.issueId ?? session.id,
|
||||||
provider: toAgentProvider(session.harness),
|
provider: toAgentProvider(session.harness),
|
||||||
kind: session.kind === "orchestrator" ? "orchestrator" : session.kind === "worker" ? "worker" : undefined,
|
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),
|
status: toSessionStatus(session.status, session.isTerminated),
|
||||||
createdAt: session.createdAt,
|
createdAt: session.createdAt,
|
||||||
updatedAt: session.updatedAt,
|
updatedAt: session.updatedAt,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue