Summary
Enforce the "working branches are per-instance-only" invariant to prevent silent data loss when two agents push to the same branch.
Context
Problem P5: Two Trinity agents can be configured to push to the same (repo, branch) tuple with no collision check. Force-pushes silently clobber each other — whoever pushed last wins.
Real incident: 2026-04-17, alpaca-vybe-live and alpaca-vybe-live-2 both pushed to trinity/alpaca-vybe-live/a702560e within 12 seconds. One agent's 50KB of state was silently overwritten.
Problem P7: generate_instance_id() is called at three sites with no cross-check:
src/backend/services/agent_service/crud.py:225
src/backend/routers/git.py:367
src/backend/services/git_service.py:405
Acceptance Criteria
Layer 0: Consolidate instance ID generation
Layer 1: Creation-time remote check
Layer 2: DB uniqueness constraint
Layer 3: Push-time guard
Observability
Technical Notes
Files to modify:
src/backend/services/git_service.py — new reserve_and_generate_instance_id, check_remote_branch_exists
src/backend/db/schema.py, db/migrations.py — partial UNIQUE index
src/backend/routers/agents.py, routers/git.py, routers/agent_config.py — validate on create/update
docker/base-image/agent_server/routers/git.py — --force-with-lease, persist last-remote-sha
- Frontend — fleet binding warnings
Migration note: Deploy validation as warnings first, fix known duplicates, then flip the constraint.
Part of Epic #381
Summary
Enforce the "working branches are per-instance-only" invariant to prevent silent data loss when two agents push to the same branch.
Context
Problem P5: Two Trinity agents can be configured to push to the same
(repo, branch)tuple with no collision check. Force-pushes silently clobber each other — whoever pushed last wins.Real incident: 2026-04-17,
alpaca-vybe-liveandalpaca-vybe-live-2both pushed totrinity/alpaca-vybe-live/a702560ewithin 12 seconds. One agent's 50KB of state was silently overwritten.Problem P7:
generate_instance_id()is called at three sites with no cross-check:src/backend/services/agent_service/crud.py:225src/backend/routers/git.py:367src/backend/services/git_service.py:405Acceptance Criteria
Layer 0: Consolidate instance ID generation
reserve_and_generate_instance_id(agent_name, github_repo)helpergit ls-remoteto verify branch doesn't exist on remoteLayer 1: Creation-time remote check
Layer 2: DB uniqueness constraint
UNIQUE(github_repo, working_branch) WHERE source_mode = 0Layer 3: Push-time guard
git push --forcewithgit push --force-with-lease=<ref>:<expected-sha>.trinity/last-remote-sha/<branch>after fetchObservability
Technical Notes
Files to modify:
src/backend/services/git_service.py— newreserve_and_generate_instance_id,check_remote_branch_existssrc/backend/db/schema.py,db/migrations.py— partial UNIQUE indexsrc/backend/routers/agents.py,routers/git.py,routers/agent_config.py— validate on create/updatedocker/base-image/agent_server/routers/git.py—--force-with-lease, persist last-remote-shaMigration note: Deploy validation as warnings first, fix known duplicates, then flip the constraint.
Part of Epic #381