monorepo-setup-worktrees
Monorepo Worktree System
Scope-based worktree isolation for parallel development streams. Each worktree gets isolated Docker services, databases, and domains.
Scopes
| Scope | Services | Containers | Create time |
|---|---|---|---|
elements |
Elements runner + dev-projects only | 2 | ~28s |
web |
Elements + telecine core (web, hasura, valkey, maintenance) | 8 | ~1:30 |
render |
Elements + telecine core + render pipeline (workers, scheduler) | ~22 | ~2:30 |
Upgrade path: elements → web → render. Downgrade not supported.
Worktree Management
All worktree operations use the unified CLI: scripts/worktree <command>.
worktree create <branch> [scope] # Create new worktree (default: web)
worktree list # List all worktrees
worktree status [branch] # Health check
worktree pause <branch> # Stop containers
worktree resume <branch> # Start containers
worktree remove <branch> [--force] # Full cleanup
worktree upgrade <branch> <scope> # Escalate scope (elements→web→render)
worktree merge <branch> # Merge branch into main (feature → main)
worktree pull # Fetch upstream, update local main, sync main → all worktrees
worktree prune [--dry-run] # Remove worktrees whose branches are fully merged into main
worktree smoke <branch> # One-shot render verification
worktree logs [branch] [options] # View logs
worktree doctor [branch] [--skills] # Diagnose issues
worktree editor-deps [branch] # Install host-side node_modules for editor tooling
worktree deps [--workspace=...] # Show dependency graph
Architecture
Repo layout
~/Editframe/
monorepo -> worktrees/main/monorepo (symlink for convenience)
worktrees/
main/
.worktree-scope # scope for main (e.g. "render")
monorepo/ # primary monorepo checkout [main]
telecine -> ../telecine # symlink
elements -> ../elements # symlink
telecine/ # primary telecine clone [main]
elements/ # primary elements clone [main]
<branch>/
.worktree-scope # scope for this branch
monorepo/ # monorepo worktree [branch]
telecine/ # telecine git worktree [branch]
elements/ # elements git worktree [branch]
The main worktree (worktrees/main/) must always be on the main branch. Never run git checkout, git switch, or any branch-switching command in the main worktree directories. All feature work lives in a dedicated branch worktree created with worktree create. An LLM agent is most likely to violate this by running git checkout <branch> directly, treating the main worktree like a normal single-checkout repo, or using merge patterns that require a prior checkout.
scripts/worktree.ts enforces this with two hard guards — do not remove or relax them:
cmdCreateasserts the main worktree is onmainbefore creating any new worktree, and always bases new branches offmain.cmdMergeasserts each repo is already onmainand merges without a checkout — never callsgit checkoutin the main worktree.
~/Editframe/monorepo is a convenience symlink and the entry point for all worktree commands.
Working in a branch worktree as an agent
An agent's working directory is fixed at launch — typically worktrees/main/monorepo. There is no persistent cd. After creating or identifying a branch worktree, every file edit, git command, and script invocation must use the full absolute path rooted at the branch worktree, not a relative path from the agent's cwd.
Given branch my-feature the worktree root is ~/Editframe/worktrees/my-feature/monorepo/. Concrete rules:
- File reads/edits: absolute paths under
~/Editframe/worktrees/my-feature/monorepo/ - Git operations:
git -C ~/Editframe/worktrees/my-feature/monorepo <command>or use theworkdirparameter in tool calls - Elements scripts:
~/Editframe/worktrees/my-feature/elements/scripts/<script> - Telecine scripts:
~/Editframe/worktrees/my-feature/telecine/scripts/<script> - Never use relative paths or paths under
worktrees/main/for branch work
To find the root for an existing branch: git worktree list | grep my-feature
EDITFRAME_DIR in scripts is always $(dirname $(dirname $(dirname $(git rev-parse --show-toplevel)))) — three levels up from the monorepo checkout path.
Shared infrastructure
editframe-postgres— single shared PostgreSQL, each worktree gets its own database (telecine-<branch>)editframe-traefik— shared reverse proxy, routes byHostheader (<branch>.localhost)telecine-runner/elements-runner— shared Docker images (not rebuilt per worktree)
Database template
telecine-template is cloned from telecine-main (304 migrations + seed data, ~0.6s clone). Template auto-refreshes when telecine/scripts/start runs migrations on main.
Port offsets
Worktree services use cksum-based port offsets (200 slots, spacing of 100) so host tools like Postico can connect. Main worktree uses standard ports.
Config scripts
telecine/scripts/worktree-config— exportsWORKTREE_ID,WORKTREE_DATABASE,WORKTREE_DOMAIN,WORKTREE_DOCKER_PROJECT_NAME, port variableselements/scripts/worktree-config— same pattern for elements.worktree-scopefile in monorepo worktree root tracks current scope
Docker Compose profiles
- No profile = core services (always start): runner, web, valkey, graphql-engine, data-connector-agent, maintenance
renderprofile: all worker services, scheduler-go, jit-transcodingdevprofile: tracing, otel-viewer, mailhog, playwrighttelecine/scripts/startreads.worktree-scopeto setCOMPOSE_PROFILES
Service startup ordering
Runner must start and npm install must complete before other services that execute application code (web, dev-projects, workers). The create and upgrade scripts handle this: up -d runner → npm install → up -d (remaining services).
Syncing main into worktrees
After PRs merge to main (which happens frequently), run worktree pull from the main worktree:
worktree pull
This does three things in sequence:
- Fetches upstream remotes for monorepo, telecine, and elements
- Merges
origin/maininto local main for telecine and elements (fast-forward when clean) - Merges local main into every active worktree branch
Conflicts in package.json, package-lock.json, and VERSION.ts are auto-resolved by accepting main's version. All other conflicts are reported and skipped — the affected worktree is left untouched for manual resolution.
To clean up worktrees whose branches have been fully merged into main across all three repos:
worktree prune # remove all fully-merged worktrees
worktree prune --dry-run # preview what would be removed
prune checks all three repos before removing anything — a worktree is only pruned when its branch is an ancestor of main in monorepo, telecine, and elements simultaneously.
Worktree lifecycle
create (elements, 28s) → upgrade (web, 63s) → upgrade (render)
↓ ↓
pause/resume pause/resume
↓
remove
Dev Server URLs
The elements dev-projects Vite server uses root: elements/dev-projects/. Files are served at the root path — not under /dev-projects/.
video.html→http://<branch>.localhost:4321/video.htmlcanvas-demo.html→http://<branch>.localhost:4321/canvas-demo.html
Never include dev-projects/ in the URL path.
dev-projects in worktrees
elements/dev-projects/ is gitignored. Worktrees would only have committed stubs without the full asset/src tree.
worktree create sets DEV_PROJECTS_HOST in the worktree's elements/.env to point at main's dev-projects. The docker-compose.yaml dev-projects service mounts this path over /packages/dev-projects, so the worktree's dev server always has the full file tree from main.
Smoke testing
worktree smoke <branch> is a one-shot render verification. It's not a persistent scope; use it as a pre-merge gate for render pipeline changes.
- Requires
weborrenderscope (errors onelements) - If scope is
web: temporarily starts render-profile services, registers a cleanup trap to stop them on exit - If scope is
render: runs against already-running services, no lifecycle management - Runs
telecine/scripts/smoke-test.tsinside the runner container withEF_HOST=http://web:3000and the worktree'sEF_TOKEN - Prints the dashboard URL (
http://<branch>.localhost:3000) for visual inspection of render outputs - Prompts to press enter before stopping render services (if they were started)
Render development workflow: stay at web scope, run unit tests directly, use worktree smoke as the merge gate rather than keeping a full render stack running all day.
scheduler-go is a pre-built Go image not managed by docker-compose. worktree smoke builds it automatically on first run. scripts/build-runner-images also builds it.
Editor tooling
Docker containers use named volumes for node_modules, which are invisible to the host filesystem. The host-side editor (VS Code, Cursor, etc.) needs its own node_modules to resolve TypeScript types and JSX intrinsics.
worktree create installs host-side deps automatically with npm install --ignore-scripts. For existing worktrees missing host types, run worktree editor-deps <branch>. worktree doctor detects and reports missing host-side types.
Troubleshooting
- Port conflict: two branches hashed to same offset. Extremely unlikely with cksum/200 slots but possible. Remove one worktree and recreate.
- Orphaned containers:
worktree doctordetects orphaned projects (containers with no matching git worktree) and prints the exactdocker rm -fcommand to clean them up. - Partial create failure: if
worktree createfails partway through, the worktree directory exists but is incomplete. Runworktree remove --force <branch>before retrying. - Stale template: run
scripts/update-template-dbto refresh from current main DB state.
More from editframe/skills
video-analysis
Analyze video files using ffprobe, mp4dump, and jq. Use when investigating video samples, keyframes, MP4 box structure, codec info, packet timing, or debugging video playback issues.
75visual-thinking
Create visual analogies by mapping relational structure from familiar domains onto unfamiliar concepts using spatial relationships to make abstract patterns concrete. Covers static diagrams AND animated video storytelling (camera choreography, race comparisons, pacing). Use when explaining complex concepts, creating analogies, designing diagrams, creating explainer animations, or revealing system structure.
71css-animations
CSS animation fill-mode requirements for Editframe timeline system. Use when creating CSS animations, debugging flashing/flickering issues, or when user mentions animation problems, fade effects, slide effects, or sequential animations.
69threejs-compositions
Integrate Three.js 3D scenes into Editframe compositions via addFrameTask. Scenes are pure functions of time, fully scrubable, and renderable to MP4. Use when creating 3D animations, WebGL content in compositions, or integrating Three.js with Editframe's timeline system.
66editor-gui
Build video editing interfaces using Editframe's GUI web components. Assemble timeline, scrubber, filmstrip, preview, and playback controls like lego bricks. Use when creating video editors, editing tools, or when user mentions timeline, scrubber, preview, playback controls, trim handles, or wants to build editing UIs.
64elements-new-package
Create a new @editframe/* workspace package in the elements monorepo and publish it to npm.
64