Appearance
Parallel Agents Workflow
1. Overview
Each parallel task gets one git worktree per repo, an isolated .env.test with a unique DB port, and a dedicated Docker Postgres container.
When multiple AI agents work simultaneously on independent tasks, they need isolated environments to avoid conflicts on:
- The same working tree: two agents writing to
bautista-backend/at the same time corrupt each other's uncommitted changes. - Shared config files:
.env.test,php/constants.php, andconstants.phpcontain environment values (DB host, port, credentials). Overwriting them mid-run breaks the other agent's test suite. - The single Docker Postgres test container bound to port
65432: only one process owns that port; a second agent trying to start or reset the same container would conflict or destroy in-progress test data.
The solution is to run each agent in its own set of git worktrees (one per affected repo), each with its own generated config pointing to a distinct port, and its own Docker container.
2. Prerequisites
- Git 2.43+ — required for
git worktreesubcommand support. - Docker — required for backend integration tests (Postgres container per task).
- Bash — all scripts are plain Bash, no external dependencies.
- All four repos checked out under
/var/www/Bautista/:/var/www/Bautista/bautista-backend//var/www/Bautista/bautista-app//var/www/Bautista/informes//var/www/Bautista/docs/
3. Quick Start
bash
# Start a new parallel task
./scripts/parallel-worktree-setup.sh fix-login-validation
# Check active tasks
./scripts/parallel-worktree-list.sh
# Clean up when done
./scripts/parallel-worktree-teardown.sh fix-login-validationIf you only need worktrees for a subset of repos, use the --repos flag:
bash
# Backend only (skip app, informes, docs worktrees)
./scripts/parallel-worktree-setup.sh fix-login-validation --repos=backend4. What Gets Created
Worktrees and generated files
| Repo | Worktree path | Config generated | Symlink created |
|---|---|---|---|
| bautista-backend | bautista-backend/.worktrees/{task}/ | .env.test (worktree root + migrations/) | vendor/ → bautista-backend/vendor/ |
| bautista-app | bautista-app/.worktrees/{task}/ | php/constants.php | node_modules/ → bautista-app/node_modules/ |
| informes | informes/.worktrees/{task}/ | constants.php | vendor/ → informes/vendor/ |
| docs | docs/.worktrees/{task}/ | (none) | (none) |
Port registry
scripts/.worktree-ports is auto-generated and gitignored. It tracks all active tasks so that teardown can release ports and remove containers. Each line is one registered task. See Section 8 for the format.
5. Script Reference
parallel-worktree-setup.sh <task-name> [--repos=all|backend|app|informes|docs]
Creates git worktrees, generates isolated config files, creates symlinks for shared dependency directories, assigns a unique DB port offset, and starts a Docker Postgres container for the task.
| Argument | Required | Description |
|---|---|---|
task-name | Yes | Identifier for the parallel task. Alphanumeric characters, hyphens, and underscores only. |
--repos | No | Comma-separated list of repos to create worktrees for. Default: all. Valid values: all, backend, app, informes, docs. |
parallel-worktree-teardown.sh <task-name> [--push]
Removes all worktrees for the task across all repos (regardless of which --repos value was used at setup), deregisters the port offset, and stops and removes the Docker container.
| Argument | Required | Description |
|---|---|---|
task-name | Yes | Must match a task registered in scripts/.worktree-ports. |
--push | No | Push all task branches to origin before removing worktrees. Includes the migrations submodule branch inside the backend worktree. |
Notes:
- Removes ALL worktrees for the task across all repos, even if setup was run with
--repos=backendoriginally. - Docker container stop/remove is best-effort: if the container is already gone, teardown continues without error.
- With
--push: branches are pushed before worktrees are removed, so the push happens from within the worktree context. - With
--push:migrationsbranch is pushed independently togit@bitbucket.org:huellasnet/migrations.gitsince it is its own submodule repo.
parallel-worktree-list.sh
No arguments. Prints a table of all active tasks with their offset, DB port, container name, and registration timestamp.
Exit codes
| Code | Meaning |
|---|---|
0 | Success |
1 | User error (invalid task name, task already registered, task not found) |
2 | Environment error (submodule init failed, required file missing) |
3 | Partial failure (some repos succeeded, at least one failed) |
6. Branch Persistence Policy
Al terminar una tarea
Cuando un agente finaliza su trabajo en un worktree, los commits quedan en la rama {task-name} del worktree. El worktree y la rama son independientes — eliminar uno no elimina el otro.
worktree eliminado ──→ rama {task-name} sigue existiendo en el repo originalOpciones al completar la tarea
Opción A — Eliminar worktree, trabajar desde la rama local
bash
# Teardown libera el worktree y el container, pero la rama persiste
./scripts/parallel-worktree-teardown.sh {task-name}
# La rama queda en el repo original — podés switchear cuando quieras
cd /var/www/Bautista/bautista-app
git checkout {task-name}
git log --oneline -5 # los commits del agente están acáOpción B — Push automático con --push al hacer teardown (recomendado para trabajo sin supervisión)
bash
# Un solo comando: push de todas las ramas + teardown
./scripts/parallel-worktree-teardown.sh {task-name} --pushEl flag --push hace push en este orden:
bautista-backend→origin/{task-name}enbautista-backend.gitbautista-app→origin/{task-name}enbautista-app.gitinformes→origin/{task-name}eninformes.git(si existe worktree)docs→origin/{task-name}enbautista.git(si existe worktree)migrations→origin/{task-name}enmigrations.git(submodule anidado dentro de backend)
Los repos sin worktree activo para esa tarea se saltan automáticamente.
Opción C — Push manual previo (control fino por repo)
bash
# Push selectivo desde el worktree antes del teardown
cd /var/www/Bautista/bautista-app/.worktrees/{task-name}
git push origin {task-name}
# Para migrations (submodule dentro de backend worktree)
cd /var/www/Bautista/bautista-backend/.worktrees/{task-name}/migrations
git push origin {task-name}
# Luego teardown sin --push (ramas ya están en remoto)
./scripts/parallel-worktree-teardown.sh {task-name}Rama en migrations
Al crear un worktree de backend, el script también crea una rama {task-name} dentro del submodule migrations/. Esto permite:
- Commitear migraciones de base de datos dentro del contexto de la tarea
- Publicar esa rama a
migrations.gitjunto con el resto al usar--push - Revisarla en PR independiente o en conjunto con
bautista-backend
Regla para agentes
Los agentes NO deben mergear la rama del worktree a develop ni a ninguna otra rama. El merge lo hace el desarrollador cuando lo decida. Al terminar la tarea, el agente solo confirma:
- Los commits están en la rama
{task-name}del worktree tsc, tests y build pasan en esa rama- Reporta el hash del último commit
- Si usó
--push: confirma que todas las ramas están publicadas en remoto
7. Agent Constraints
Agents working inside a parallel worktree MUST follow these rules:
Use the worktree path for all writes, not the main repo path. Example: write to
bautista-backend/.worktrees/fix-login-validation/src/..., not tobautista-backend/src/....Do NOT run
composer install,composer update, orcomposer requireinside a worktree that has a symlinkedvendor/directory. The symlink points to the shared vendor in the main repo. Running Composer there would overwrite or corrupt that shared directory, breaking all other worktrees and the main working tree. If a dependency change is required, create a dedicated worktree with a real (non-symlinked) vendor copy.Do NOT run
npm install,npm ci, ornpm updateinside a worktree that has a symlinkednode_modules/directory. Same reason as above — it would corrupt the sharednode_modules/used bybautista-app/.Run teardown when done. Do not leave worktrees and Docker containers behind. Port offsets accumulate in the registry and are not released until teardown runs.
Do NOT edit
scripts/.worktree-portsdirectly. This file is managed exclusively byparallel-worktree-setup.shandparallel-worktree-teardown.sh. Manual edits can desync the port registry and cause offset collisions or orphaned containers.Do NOT run
git worktree addmanually inside the worktree directories. Always use the setup script, which handles config generation, symlinks, port assignment, and container startup as a coordinated unit.
8. Port Assignment
The main working tree uses port 65432 for its Docker Postgres test container. Each parallel task is assigned a unique offset:
- Base port:
65432(main working tree) - Offset range:
1to99(maximum 99 concurrent parallel tasks) - Offset assignment: the minimum unused offset found in
scripts/.worktree-ports
Example: two concurrent tasks, teardown, and reuse
Task A setup → offset 1 → port 65433, container postgres_bautista_testing_task-a
Task B setup → offset 2 → port 65434, container postgres_bautista_testing_task-b
Task A teardown → offset 1 released
Task C setup → offset 1 reused → port 65433, container postgres_bautista_testing_task-cTask C reuses offset 1 because it was freed by Task A's teardown, even though Task B (offset 2) is still active.
Registry file format
# scripts/.worktree-ports (managed by scripts — do not edit manually)
task-a 1 1740000100
task-b 2 1740000200Columns: task-name, offset, unix timestamp of registration.
9. Troubleshooting
"Error: task already registered"
Run ./scripts/parallel-worktree-list.sh to see all active tasks.
- If the task is legitimately still running in another agent: use a different task name.
- If the task is stale (the worktree no longer exists on disk but the registry entry is still there): run teardown to clean up the registry entry and any leftover containers:bash
./scripts/parallel-worktree-teardown.sh {task-name} - If you want a completely fresh start for the same logical change: use a different task name (e.g., append
-v2).
"Orphaned worktree found"
The worktree directory exists on disk but the task name is not present in scripts/.worktree-ports. This can happen if the setup script was interrupted before writing to the registry.
Clean up the orphaned worktree manually:
bash
git -C /var/www/Bautista/bautista-backend worktree remove .worktrees/{name} --force
# repeat for other repos as needed
git -C /var/www/Bautista/bautista-app worktree remove .worktrees/{name} --force
git -C /var/www/Bautista/informes worktree remove .worktrees/{name} --force
git -C /var/www/Bautista/docs worktree remove .worktrees/{name} --forceThen re-run setup with the desired task name.
"Docker container still running after teardown"
Teardown stops Docker containers best-effort but does not fail if the container is absent or already stopped. To check for lingering containers:
bash
docker ps | grep bautista_testing_To manually stop a specific container:
bash
docker stop postgres_bautista_testing_{task-name}"Integration tests fail: connection refused"
The test database connection is failing. Diagnose in order:
- Check whether the Docker container is running for this worktree:bash
docker ps | grep {task-name} - If not running:
vendor/bin/phpunit(run from inside the worktree) triggers container startup viaBaseEnvironmentTest. Verify this base class is being invoked. - Check that
.env.testin the worktree root has the correct isolated port — it should beDB_PORT=6543NwhereNis the offset (not65432, which belongs to the main working tree):bashgrep DB_PORT /var/www/Bautista/bautista-backend/.worktrees/{task-name}/.env.test - Verify that
DOCKER_COMPOSE_PATHin that same.env.testis an absolute path pointing inside the worktree, not the main repo.