How Git Branch Attribution Works Per Prompt in Background Agents
Background Agents manages Git branch attribution at the session level, where a session_branch WebSocket message updates the branch state for the entire session, and each prompt automatically inherits this branch rather than carrying explicit branch metadata in its payload.
The ColeMurray/background-agents repository implements a session-centric architecture for Git branch management that ensures consistency across multiple prompts. Unlike systems that embed branch information in each prompt request, this approach centralizes branch attribution in the session state, allowing agents to execute prompts against the currently selected branch without per-prompt configuration.
Session-Level Branch State Management
In the Background Agents system, branch selection occurs during session initialization and persists across all subsequent prompts. When a user selects a branch in the web interface, the system dispatches a session_branch message to the control-plane WebSocket rather than attaching branch data to individual prompt requests.
Dispatching Branch Updates via WebSocket
The branch selection UI utilizes the useBranches hook to fetch available branches and sends updates through a dedicated API endpoint. In packages/web/src/hooks/use-session-target-picker.ts, the selection handler posts the branch change to the session:
// packages/web/src/hooks/use-session-target-picker.ts
const { branches, loading: loadingBranches } = useBranches(repoOwner, repoName);
// ...
setSelectedBranch = (branch: string) => {
// UI calls the control‑plane API to update the session branch
fetch(`/api/sessions/${sessionId}/branch`, {
method: "POST",
body: JSON.stringify({ branchName: branch })
});
};
Reducer State Synchronization
The session socket reducer in packages/web/src/lib/session-socket/reducer.ts processes the session_branch message to maintain synchronized branch state across both the scalar session property and repository-specific entries:
// packages/web/src/lib/session-socket/reducer.ts
case "session_branch":
// keep scalar branchName and repo-specific branchName in sync
const { branchName, repoOwner, repoName } = message;
const repositories = prev.repositories?.map((repo, i) =>
repo.repoOwner === repoOwner && repo.repoName === repoName
? { ...repo, branchName }
: repo
);
return {
...prev,
branchName,
repositories,
// also update the first repo if it is the default target
...(targetIndex === 0 ? { branchName } : {})
};
This ensures that multi-repository sessions maintain consistent branch attribution across all configured repositories.
Prompt Payload Structure and Branch Absence
The prompt schema explicitly excludes branch fields, confirming that branch attribution is not handled per-prompt but rather inherited from session state. In packages/shared/src/types/session-api.ts, the prompt type definition contains only the prompt text:
// packages/shared/src/types/session-api.ts
// Defines the prompt schema - no branch field included
prompt: z.string()
When the client sends a prompt to the server via the /sessions/[id]/prompt route defined in packages/web/src/app/api/sessions/[id]/prompt/route.ts, the request body contains only the prompt text without any Git metadata:
// packages/web/src/app/api/sessions/[id]/prompt/route.ts
const response = await controlPlaneFetch(`/sessions/${sessionId}/prompt`, {
method: "POST",
body: JSON.stringify({ prompt: body.prompt })
});
How Agents Access Branch Information
Downstream agents retrieve the branch from the session state when executing operations such as code checkout, image building, or file fetching. The SCM utilities in packages/web/src/lib/scm.ts demonstrate how the system generates URLs using the session-stored branch:
// packages/web/src/lib/scm.ts
export function getScmBranchUrl(owner: string, name: string, branch: string): string {
const encodedBranch = encodeURIComponent(branch);
return `https://github.com/${owner}/${name}/tree/${encodedBranch}`;
}
This approach ensures that all prompts within a session automatically execute against the same branch unless the user explicitly initiates a branch change through the WebSocket message protocol.
Summary
- Session-level attribution: Git branches are stored in session state via
session_branchWebSocket messages, not in individual prompt payloads. - State synchronization: The reducer in
packages/web/src/lib/session-socket/reducer.tsupdates both scalar and repository-specificbranchNameproperties to maintain consistency across multi-repo sessions. - Prompt inheritance: Prompts defined in
packages/shared/src/types/session-api.tscarry only text content, inheriting branch context from the session state at execution time. - Agent consumption: Background agents read the
branchNamefrom session state when performing SCM operations, ensuring consistent execution across all prompts in the session.
Frequently Asked Questions
Does each prompt in Background Agents carry its own Git branch metadata?
No. According to the source code in packages/shared/src/types/session-api.ts, the prompt schema contains only a prompt: z.string() field. Branch information is stored at the session level in the reducer state and inherited by all prompts within that session.
How does the UI communicate branch changes to the backend?
The web interface sends a POST request to /api/sessions/${sessionId}/branch with the new branch name, which ultimately triggers a session_branch WebSocket message. The reducer in packages/web/src/lib/session-socket/reducer.ts handles this message to update the session state.
What happens to branch attribution when working with multiple repositories?
The reducer synchronizes the branchName across all entries in the state.repositories array. When a branch change occurs, it updates both the scalar branchName property and the specific repository entry, ensuring that multi-repo sessions maintain consistent branch attribution across all configured repositories.
Where do agents retrieve the branch information when executing prompts?
Agents read the branchName directly from the session state object. The SCM utilities in packages/web/src/lib/scm.ts use this value to generate provider-specific URLs and perform checkout operations, rather than extracting branch data from the prompt payload itself.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →