Skip to content

Session detail shows synthetic branch instead of actual worker branch #356

@areycruzer

Description

@areycruzer

Summary

When a worker is created with a custom branch from the New Task flow, the daemon creates the correct git worktree branch, but the session detail UI displays a synthetic session/<session-id> branch instead of the real branch.

This makes the session topbar and overview panel misleading: users cannot tell which branch the agent is actually working on, even though the backend has already persisted the correct branch in session metadata.

Severity

P2 user-visible correctness bug.

This does not appear to corrupt worktrees or block task creation, but it shows incorrect branch information in the main session view.

Reproduced On

  • Repo: upstream main
  • Local checkout: clean before/after QA
  • App mode: daemon + renderer against isolated local state
  • Date reproduced: 2026-06-21
  • Temp state only: isolated local temp directory, referred to below as <qa-temp-dir>
  • No live GitHub write flows or real user projects used for reproduction

Repro Steps

  1. Start the daemon with isolated state, for example:

    AO_DATA_DIR=<qa-temp-dir>/ao-data \
    AO_RUN_FILE=<qa-temp-dir>/running.json \
    AO_PORT=31001 \
    AO_TELEMETRY_EVENTS=off \
    AO_TELEMETRY_METRICS=off \
    AO_TELEMETRY_REMOTE=off \
    go run ./cmd/ao daemon
  2. Start the renderer against that daemon.

  3. Add a local temp git project.

  4. Open the app's New Task flow.

  5. Create a worker with:

    • Title: QA modal worker
    • Branch: qa/modal-worker
    • Agent: codex
    • Brief: any harmless prompt
  6. Open the created session detail page.

Expected Behavior

The session detail page should show the actual branch created for the worker:

qa/modal-worker

This should be visible anywhere the session branch is rendered, including the session topbar and overview panel.

Actual Behavior

The session detail page shows:

session/demo-app-3

The underlying worktree is actually on the requested branch:

$ git -C <qa-temp-dir>/ao-data/worktrees/demo-app/demo-app-3 symbolic-ref --short HEAD
qa/modal-worker

The daemon API response for the same session does not expose any branch field, so the frontend has no real branch to display:

{
  "session": {
    "id": "demo-app-3",
    "projectId": "demo-app",
    "issueId": "QA modal worker",
    "kind": "worker",
    "harness": "codex",
    "activity": {
      "state": "active",
      "lastActivityAt": "2026-06-21T06:05:01.494546Z"
    },
    "isTerminated": false,
    "createdAt": "2026-06-21T06:04:51.787185Z",
    "updatedAt": "2026-06-21T06:05:01.494546Z",
    "status": "working",
    "terminalHandleId": "demo-app-3/terminal_0",
    "prs": []
  }
}

Screenshot evidence:

The UI shows session/demo-app-3 in the topbar and Overview > Branch, while the actual worktree branch is qa/modal-worker.

Screenshot attached in the first issue comment: https://github.com/user-attachments/assets/e2629441-fab8-42f1-8fa9-02194c9d86ec

Code Path / Likely Root Cause

The backend accepts and uses the requested branch during spawn:

  • backend/internal/httpd/controllers/dto.go
    • SpawnSessionRequest includes Branch string json:"branch,omitempty".
  • backend/internal/httpd/controllers/sessions.go
    • spawn passes Branch: in.Branch into ports.SpawnConfig.
  • backend/internal/session_manager/manager.go
    • Spawn uses cfg.Branch, falling back only when it is empty.
    • It then creates the workspace with that branch.
    • It persists domain.SessionMetadata{Branch: ws.Branch, ...}.
  • backend/internal/domain/session.go
    • SessionMetadata contains Branch string.

However, the read model does not expose that persisted branch:

  • backend/internal/domain/session.go
    • SessionRecord.Metadata is json:"-".
  • backend/internal/httpd/controllers/dto.go
    • SessionView embeds domain.Session, so the metadata branch is omitted from the API response.

The frontend then fabricates a branch because the API does not provide one:

  • frontend/src/renderer/hooks/useWorkspaceQuery.ts

    • currently maps every daemon-backed session with:

      branch: `session/${session.id}`,
  • frontend/src/renderer/types/workspace.ts

    • WorkspaceSession.branch is required, so the synthetic fallback is always displayed.

The New Task flow does send the user-entered branch correctly:

  • frontend/src/renderer/components/NewTaskDialog.tsx
    • sends branch: cleanBranch || undefined in the POST /api/v1/sessions body.

So the bug appears to be in the read/API/display path, not in the form submission path or workspace creation path.

Suggested Fix Shape

Expose the persisted branch on the session API read model, then consume it in the frontend.

Possible backend shape:

  • Add a top-level branch field to SessionView, sourced from domain.Session.Metadata.Branch.
  • Keep the current metadata encapsulation if desired; the API only needs the display-safe branch string.
  • Regenerate OpenAPI/frontend schema with npm run api if the wire contract changes.

Possible frontend shape:

branch: session.branch ?? `session/${session.id}`,

The fallback can remain defensive for older/partial data, but current daemon-backed sessions should display the real persisted branch.

Suggested Tests

Backend:

  • Add or extend a session controller/read-model test to verify that a session with Metadata.Branch serializes a branch field.
  • If there is already a spawn/get integration-style test, assert that a custom spawn branch round-trips through GET /api/v1/sessions/{id} or GET /api/v1/sessions.

Frontend:

  • Add/extend useWorkspaceQuery coverage so an API session with branch: "qa/modal-worker" maps to WorkspaceSession.branch === "qa/modal-worker".
  • Add a fallback test for missing branch, if the synthetic session/<id> fallback is intentionally retained.

Verification commands for the fix:

cd backend && go test ./internal/httpd/... ./internal/session_manager/...
npm run api
npm --prefix frontend run typecheck
npm --prefix frontend test -- useWorkspaceQuery

Duplicate / Overlap Check

I searched current issues and PRs for:

  • branch session UI display
  • New task branch
  • session branch
  • exact evidence/title terms like session/demo-app and qa/modal-worker

No existing issue matched this exact bug.

Closest related work checked:

Contribution Value

This should be a good focused contribution because:

  • It is user-visible and easy to verify locally.
  • It does not require private credentials or live GitHub PR creation.
  • The backend already stores the correct source of truth.
  • The fix should stay inside existing daemon/API/frontend mapping boundaries.
  • The PR can be small: expose one read-model field, regenerate schema, update the frontend mapper, and add regression coverage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdaemonHTTP daemon lanefrontendElectron frontend lanegood first issueGood for newcomerspriority: mediumFix when convenient

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions