Sessions Git Protocol
Session-Aware Git Protocol (SAGP) — mandatory rules for concurrent session management and git discipline
Session-Aware Git Protocol (SAGP) v1.3
Purpose: Ensure agents working concurrently on different sessions never mix changes, always commit logically, and keep the repository clean and navigable.
Concurrent lanes (v1.3): A branch is a shared lane; session ownership is
[S{N}.{sub}]on each commit. Several sessions may use one branch (e.g. range lanes likesession-264-269_new). Normative model: session-git-concurrent-lanes-spec.md. Manifests stay one file per session (sessions/manifests/S{N}.json); only Git handling is more flexible.
1. Session Lifecycle — The 4 Mandatory Steps
Every agent MUST follow this lifecycle for every task:
INIT → WORK → COMMIT → CLOSE
Step 1: INIT (before writing any code)
1. Call: preflight({ task: "...", sessionNumber: N, subSessionNumber: N.X })
OR: session({ action: "set", sessionNumber: N, subSessionNumber: X })
2. Verify git branch (lane):
- Run: `git branch --show-current`
- The branch must **plausibly relate** to session **N** — e.g. `session-{N}`, or a **range lane** whose name includes **N** (e.g. `session-264-269_new` for session 269). MCP preflight uses a **multi-session** policy by default.
- If you need a new lane: `git checkout -b session-{A}-{B}` or `session-{N}` as appropriate; coordinate with teammates on shared lanes.
- If you must switch lanes: stash/commit first, then `git checkout <lane>` (see §5).
3. Update sub-session: always increment subSessionNumber from the last known value
- Check: session({ action: "get_info" }) or read sessions/manifests/S{N}.json
- Your sub-session = last sub + 1 (e.g., if last was 144.5, yours is 144.6)
Step 2: WORK (implement the task)
- All changes go to your current lane (often a shared
session-*or range branch — see session-git-concurrent-lanes-spec.md) - Group related files logically (don't mix unrelated features)
- Track which files you create/modify for the commit; every commit uses
[S{N}.{sub}]for the session that owns that change
Step 3: COMMIT (after completing a sub-session or logical unit)
1. Stage ONLY files belonging to your sub-session:
git add <specific-files>
2. Commit with structured message:
git commit -m "[S{N}.{sub}] type: description"
Examples:
- [S144.3] feat: sandbox provider abstraction with strategy pattern
- [S144.4] fix: observatory dashboard data binding
- [S139.6] test: A2A protocol integration tests
3. Update session manifest:
session({ action: "set", sessionNumber: N, subSessionNumber: X })
Step 4: CLOSE (when the sub-session is complete)
1. Verify all changes are committed: git status (should be clean for your files)
2. Log the decision: log_decision({ ... })
3. Report outcome: outcome({ action: "report", ... })
4. If session is fully complete:
- Land on `main` via **PR** (or controlled merge policy). PR title and merge/squash messages must preserve **session tags** — see [session-git-concurrent-lanes-spec.md](./session-git-concurrent-lanes-spec.md) (§3.5–3.6).
- OR: leave the lane for manual review
2. Git Branch Protocol
Branch naming (lanes)
| Branch | Purpose | Lifetime |
|---|---|---|
main |
Stable, tested code | Permanent |
session-{N} |
Lane often used for a single session or as a simple name | Until merged to main |
session-{A}-{B} / session-{A}-{B}_{suffix} |
Range / shared lane — several concurrent sessions may commit here | Until merged to main |
- Several sessions MAY share one lane. Sub-sessions are still tracked via
[S{N}.{sub}]commit messages, not by creating one branch per session. - Branch name = coarse scope (which line of history). Commit tag = ownership for that change.
- MCP: default branch check is multi-session (range-aware). Set
MCP_GIT_BRANCH_POLICY=strictonly if you require legacysession-{N}-only matching.
Full normative rules: session-git-concurrent-lanes-spec.md
Branch rules
- NEVER work on
maindirectly — use asession-*lane (including range lanes) - Do not land unrelated work without attribution — if the lane is shared, use the correct
[S{N}.{sub}]per commit; split commits when needed - Check your lane before every commit:
git branch --show-current - Create a lane if it doesn't exist: e.g.
git checkout -b session-{N}or a range lane agreed with your team
When you need a different lane
# Save current work if you have uncommitted changes
git stash push -m "S{CURRENT}.{sub}: WIP description"
# Switch to the target lane (session-{M} or shared range branch)
git checkout <target-lane>
# Continue working (or pop a stash if resuming)
3. Commit Convention
Message Format
[S{session}.{sub}] type: concise description
Optional body with details.
Types
| Type | When |
|---|---|
feat |
New feature or capability |
fix |
Bug fix |
refactor |
Code restructuring without behavior change |
test |
Adding or updating tests |
docs |
Documentation only |
chore |
Config, tooling, dependencies |
style |
Formatting, SCSS, visual-only changes |
Rules
- One commit per sub-session (preferred) or per logical unit
- Never accumulate uncommitted changes across sub-sessions — commit before starting a new sub-session
- Never commit files from another session — if you find orphaned files, stash them for the right session
- Cross-cutting files (CLAUDE.md, AGENTS.md, history.json) commit on the current session branch — they merge naturally
Examples
# Good: specific, traceable
git commit -m "[S144.3] feat: sandbox provider abstraction with strategy pattern"
git commit -m "[S144.4] feat: observability dashboard backend + frontend"
git commit -m "[S139.6] test: A2A protocol integration tests"
# Bad: vague, no session tag
git commit -m "updates"
git commit -m "fix stuff"
git commit -m "WIP"
4. Stash Protocol
When to Stash
| Situation | Action |
|---|---|
| Switching to a different session | git stash push -m "S{N}.{sub}: description" |
| Found orphaned files from another session | git stash push -m "S{N}: orphaned files" -- file1 file2 |
| Need clean working tree for build/test | git stash push -m "S{N}.{sub}: temp stash for testing" |
When NOT to Stash
| Situation | Do This Instead |
|---|---|
| Completed work | Commit it |
| Permanent storage | Commit to the session branch |
| Backup | Push the session branch to remote |
Stash Naming Convention
S{session}.{sub}: {description}
Examples:
S144.3: WIP sandbox providersS141.15: orphaned desktop app filesS139.6: A2A tests ready for review
Stash Commands Quick Reference
# Stash all uncommitted changes
git stash push -m "S{N}.{sub}: description"
# Stash specific files only
git stash push -m "S{N}.{sub}: description" -- path/to/file1 path/to/file2
# List stashes
git stash list
# Pop the most recent stash
git stash pop
# Pop a specific stash
git stash pop stash@{2}
# Apply without removing from stash list (safer)
git stash apply stash@{0}
# Drop a stash after confirming it's applied
git stash drop stash@{0}
5. Concurrent Agent Workflow
Scenario: Two agents working simultaneously (different sessions)
Option A — separate lanes: each checks out a different session-{N} or range lane; no overlap on working tree if branches differ.
Option B — shared lane (common): both work on the same range lane (e.g. session-264-269_new). No conflicts as long as they commit with distinct [S{N}.{sub}] tags and coordinate file overlap like any shared branch.
Agent A (S144) Agent B (Session 145)
───────────────────── ─────────────────────
1. preflight(S144) 1. preflight(S145)
2. git checkout <lane-144> 2. git checkout <lane-145 or same shared lane>
3. Work + commit [S144.x] 3. Work + commit [S145.x]
Scenario: Same agent switching between sessions
1. Working on S144.3 (lane: session-144 or a shared range branch)
2. Need to fix something in S141
# Save S144 work
git stash push -m "S144.3: WIP sandbox providers"
# Switch to S141's lane
git checkout session-141 # or the lane where S141 work lives
# Fix the issue
git add <files> && git commit -m "[S141.15] fix: tauri config update"
# Return to S144 lane
git checkout session-144 # or previous shared lane
git stash pop
# Continue working on S144.3
Scenario: Discovered orphaned files from another session
PREFERRED (safe): Commit orphaned files from the current branch using cherry-pick
1. Working on S144 (branch: session-144)
2. Notice files that belong to S139 (e.g., A2A tests)
# Commit ONLY the orphaned files on your current branch with a clear tag
git add APIs/backend-vitto-api/src/app/a2a/__tests__/
git commit -m "[S139-orphan] test: A2A integration tests (to cherry-pick to session-139)"
# Now cherry-pick to the target branch WITHOUT checking it out:
git stash push --include-untracked -m "SAGP: save state before cherry-pick"
git checkout session-139
git cherry-pick <commit-hash>
git checkout session-144
git stash pop
# Remove the orphan commit from session-144 (it now lives on session-139)
git reset HEAD~1
ALTERNATIVE (when cherry-pick is overkill for small files): Leave a note
# Stash the orphaned files with a descriptive name
git stash push -m "S139: orphaned A2A tests - MOVE TO session-139" -- <files>
# DO NOT checkout old branches in the live workspace
# Leave the stash for a dedicated cleanup pass or the session owner
CRITICAL: Never checkout old branches in the live workspace
Checking out branches that are far behind the current branch causes the IDE to show thousands of "changes" (build artifacts, generated files, new directories). This is terrifying and risks accidental commits or panic-driven data loss.
If you MUST checkout an old branch:
- First verify:
git log --oneline session-{OLD} -1— how old is it? - If more than ~5 commits behind: use cherry-pick instead of checkout
- If you must checkout: do it, commit fast, checkout back immediately
- NEVER run
git add -Aorgit add .on an old branch — you'll stage build artifacts
6. Session Resolution Protocol (Cleaning Up Mixed Changes)
When uncommitted changes from multiple sessions are mixed on one branch, follow this protocol:
Step 1: Audit
# See all uncommitted changes
git status
# Read session manifests to understand what each session was working on
cat sessions/manifests/S144.json | jq '.subSessions[-1].focus'
Step 2: Group
Categorize every changed file by session. Use these heuristics:
- File path patterns: sandbox/* → likely S144.3, a2a/* → likely S139
- Session manifest descriptions: match file purposes to session goals
- Co-occurrence: files modified together usually belong to the same session
Step 3: Isolate (stash per session)
# For each non-current session, stash its files:
git stash push -m "S141: desktop app files" -- \
apps/vitto-desktop/src-tauri/tauri.conf.json \
deployment/cloudflare/vitto-updater-worker/
git stash push -m "S139: A2A tests" -- \
APIs/backend-vitto-api/src/app/a2a/__tests__/
Step 4: Commit current session
# Now only current session files remain
git add -A
git commit -m "[S144.3] feat: sandbox provider abstraction"
Step 5: Distribute stashes to proper branches
# For each stashed session:
git checkout session-141
git stash pop # or apply stash@{N}
git add -A && git commit -m "[S141.15] feat: cloudflare updater worker"
git checkout session-139
git stash pop
git add -A && git commit -m "[S139.6] test: A2A integration tests"
# Return to your session
git checkout session-144
7. Sub-Session Numbering Rules
How sub-sessions work
- Session 144 has sub-sessions: 144.1, 144.2, 144.3, 144.4, 144.5, ...
- The sub-session number is an integer stored separately (1, 2, 3, ...)
- Displayed as decimal:
{sessionNumber}.{subSessionNumber}→144.3
Rules for agents
- Always check the current sub-session before starting work:
session({ action: "get_info" }) - Increment by 1 from the last known sub-session
- Never reuse a sub-session number — if 144.5 exists, use 144.6
- Never skip — don't jump from 144.3 to 144.7
- Set it explicitly when starting:
session({ action: "set", sessionNumber: 144, subSessionNumber: 6 })
Multiple agents on the same session
If two agents need to work on the same session:
- Agent A takes the next sub-session (e.g., 144.6)
- Agent B takes the one after (e.g., 144.7)
- They MUST coordinate by checking
get_infofirst - Both commit to
session-144but with different[S144.6]and[S144.7]tags
8. Quick Reference Card
Before ANY work
# 1. Set session in MCP
preflight({ task: "...", sessionNumber: N })
# 2. Verify lane (must relate to session N — including range lanes)
git branch --show-current
git checkout <lane> # Switch if needed (session-{N} or session-{A}-{B}_*)
git checkout -b <lane> # Create if new
After completing work
# 1. Stage and commit
git add <your-files>
git commit -m "[S{N}.{sub}] type: description"
# 2. Update MCP
log_decision({ ... })
outcome({ action: "report", ... })
Switching sessions
# 1. Stash current work
git stash push -m "S{N}.{sub}: WIP description"
# 2. Switch lane
git checkout <lane-for-other-session>
# 3. When returning
git checkout <your-lane>
git stash pop
Cleaning up mixed changes
# 1. Stash foreign files
git stash push -m "S{OTHER}: description" -- <foreign-files>
# 2. Commit your files on the current branch
git add <your-files> && git commit -m "[S{N}.{sub}] ..."
# 3. Leave foreign stashes for later — DO NOT checkout old branches here
# The session owner or a dedicated cleanup pass will handle them
9. Crash Recovery Protocol
If an agent crashes mid-operation, the next agent MUST run this diagnostic before doing anything:
# 1. Where am I?
git branch --show-current
# 2. What's in the working tree?
git status --porcelain --untracked-files=no | wc -l # tracked changes
git status --porcelain --untracked-files=no # list them
# 3. What stashes exist?
git stash list
# 4. What was the last commit?
git log --oneline -3
# 5. Am I on the wrong lane? (common after crash)
# If yes, checkout the correct lane for this session:
git checkout <lane>
Recovery Scenarios
Scenario A: Agent crashed during stash push
- Check
git stash list— if the stash exists, it was saved - Check
git status— if files are still in working tree, stash failed - Resolution: retry the stash or commit directly
Scenario B: Agent crashed during branch checkout
- Check
git branch --show-current— which branch are you on? - If on the wrong branch with thousands of untracked files:
git checkout session-{CORRECT}immediately - Those "changes" are build artifacts, not real modifications
Scenario C: Agent crashed during commit
- Check
git log --oneline -1— did the commit succeed? - If yes: continue from next step
- If no: re-stage and re-commit
Scenario D: Unknown state
- If a BACKUP stash exists (
git stash list→ look for "BACKUP"): everything is safe - Checkout the main working lane:
git checkout <lane> - Run
git statusto assess - If truly lost:
git stash apply stash@{BACKUP_INDEX}restores everything
10. Anti-Patterns (NEVER Do These)
Note: Section numbers 10-13 were renumbered in SAGP v1.2 to fix a duplicate §10. v1.3 adds concurrent lanes; §14 replaces the old
integrate/*chapter.
| Anti-Pattern | Why It's Bad | Do This Instead |
|---|---|---|
| Accumulate changes across sub-sessions without committing | Creates the exact mess we're trying to avoid | Commit after each sub-session |
Work on main directly |
Blocks other agents, risks stable code | Use a session-* lane (including range lanes) |
| Commit files from multiple sessions in one commit | Makes rollback and tracking impossible | One session per commit |
| Leave work in stashes permanently | Stashes get lost and confusing | Pop and commit within the same task |
Skip preflight / session set |
MCP loses track of who's doing what | Always init the session first |
| Reuse or skip sub-session numbers | Breaks the session timeline | Always increment by 1 |
Commit without [S{N}.{sub}] prefix |
Breaks traceability | Always tag commits |
Push to main without merging through a PR / policy |
Bypasses review, breaks history | Open PR from session lane; tag merge per session-git-concurrent-lanes-spec.md |
| Checkout old branches in the live workspace | IDE shows thousands of build artifacts as "changes", causes panic and potential mistakes | Use cherry-pick or leave stashes for later |
Run git add -A or git add . on an old branch |
Stages build artifacts and unrelated files | Always git add <specific-files> |
| Perform multi-step git operations without a backup stash | If agent crashes mid-operation, recovery is harder | Always git stash push --include-untracked -m "BACKUP: ..." first |
11. Merge Protocol — Converging Session Branches
When multiple session branches diverge, they need to converge. Follow this protocol:
When to merge
| Situation | Action |
|---|---|
| Session completed, tested, ready | Merge session branch → main |
| Multiple session branches diverged from same base | Merge older sessions INTO the most advanced branch |
| Need changes from another session | git cherry-pick <commit> (do NOT merge the whole branch) |
How to merge safely
# 1. Pre-flight check: will there be conflicts?
git diff --name-only session-144-new...session-141 # unique files only
git log --oneline session-141 -3 # understand what's on it
# 2. If no overlapping files → merge directly:
git merge session-141 -m "merge: absorb S141 description into session-{N}"
# 3. If conflicts are expected → dry-run first:
git merge --no-commit --no-ff session-141
git diff --cached --stat # review what will change
git merge --abort # cancel if unhappy
# OR: git commit -m "merge: ..." # accept if clean
Merge commit message format
merge: absorb S{OTHER} {brief description} into session-{N}
Merge direction rules
- Older INTO newer — always merge the older/smaller branch into the most advanced one
- Never merge
maininto a session branch — rebase instead if needed - Session branch →
main— only when the session is fully complete and tested - After merging, the absorbed branch can be deleted:
git branch -d session-{OTHER}
Cherry-pick for surgical transfers
When you only need ONE commit from another branch (not the whole branch):
# Find the commit hash
git log --oneline session-141 -5
# Cherry-pick it onto your branch
git cherry-pick <commit-hash>
This avoids merge commits entirely and keeps history linear.
12. Stash Naming Convention
Format
S{session}.{sub}: {verb} {what} [{status}]
Status tags
| Tag | Meaning |
|---|---|
WIP |
Work in progress, will return to this |
READY |
Complete, ready to commit on target branch |
ORPHAN |
Found on wrong branch, needs moving |
BACKUP |
Safety snapshot, do not drop until verified |
Examples
S144.3: WIP sandbox provider refactor
S141: READY cloudflare updater worker + tauri config
S139: ORPHAN A2A tests found on session-144
BACKUP: full working tree before SAGP cleanup - DO NOT DROP
Rules
- Stashes are TEMPORARY — never leave them longer than 1 session
- Always use
-m— unnamed stashes are impossible to identify - One concern per stash — don't mix unrelated files in a single stash
- Pop, don't apply —
git stash popremoves the stash after applying; useapplyonly when you're uncertain - Git does NOT support renaming stashes — if a stash has a bad name, pop it, then re-push with a better name
13. Session Types Reference
This workspace has three distinct session systems:
| System | ID Format | Where | Used By |
|---|---|---|---|
| Dev Sessions | {workspace}-{N}.{sub} |
sessions/manifests/S{N}.json, MongoDB ai-agents.sessions |
MCP tools, agents, session-manager.js |
| Chat Sessions | UUID | MongoDB via backend-ai-api /sessions/* |
Frontend chat UI |
| Agent Sessions | {userId}:{spaceId} |
MongoDB via backend-vitto-api | Vitto agent conversations |
This protocol applies to Dev Sessions only. Chat and Agent sessions are managed by their respective backends and don't interact with git.
Dev Session Data Flow
Agent calls preflight/set_session
→ MCP updates enforced session cache (process-scoped, 24h TTL)
→ MCP updates sessions/history.json (activeSessions[])
→ MCP syncs to MongoDB ai-agents.sessions (async)
→ Agent checks out a **lane** (`session-{N}` or a **range / shared** branch — see §2)
→ Agent works and commits with [S{N}.{sub}] tags
→ Agent calls log_decision + outcome
→ MCP records in manifest + MongoDB
14. Concurrent lanes & integrate/* (v1.3)
Concurrent lanes
Several sessions may share one Git branch (often a range lane like session-264-269_new). Ownership is always [S{N}.{sub}] per commit, not the branch name alone.
Normative spec: session-git-concurrent-lanes-spec.md
Merges to main
Use PRs (or your merge policy). PR titles and merge/squash messages must carry session traceability ([S{N}.{sub}] or an explicit session list). Details: concurrent-lanes spec §3.5–3.6.
Deprecated: integrate/{N}-{M} branches
The old disposable integrate/* workflow is deprecated in v1.3. Shared range lanes plus PR → main replace it. For a one-off local combine, use any temporary branch name—no integrate/* convention.
Legacy: Long-form integrate/* instructions were removed from this doc to avoid contradicting v1.3; recover from git history of this file prior to 2026-03-25 if needed.