|
| 1 | +# Persistent Multi-Agent Worktree Setup |
| 2 | + |
| 3 | +You are setting up a persistent multi-agent worktree environment for Claude and Codex. |
| 4 | + |
| 5 | +## Requirements |
| 6 | + |
| 7 | +- Create 6 permanent worktrees under `.worktrees/`: |
| 8 | + - `.worktrees/agent-collector` |
| 9 | + - `.worktrees/agent-generator` |
| 10 | + - `.worktrees/agent-schema` |
| 11 | + - `.worktrees/agent-investigator` |
| 12 | + - `.worktrees/agent-architect` |
| 13 | + - `.worktrees/agent-gardener` |
| 14 | +- Branch policy: each worktree is on `worktree/<worktree-name>`. |
| 15 | +- AGENTS is authoritative for each worktree; `CLAUDE.md` and `.codex/agent.md` mirror the read-forward policy. |
| 16 | +- Include verification commands in infrastructure files: |
| 17 | + - `dotnet build` |
| 18 | + - `dotnet test` |
| 19 | +- Do not modify existing repository source code. |
| 20 | +- Idempotent: safe to run multiple times. |
| 21 | +- Fail fast on conflicts. |
| 22 | + |
| 23 | +## Execution rules |
| 24 | + |
| 25 | +- Use only `bash`, `git`, and filesystem operations. |
| 26 | +- If a worktree path exists but is not a registered worktree, abort. |
| 27 | +- If worktree path and branch are already correct, reuse and sync files. |
| 28 | +- Do not run model-specific tooling. This is shell/git driven. |
| 29 | + |
| 30 | +## Architecture constraints |
| 31 | + |
| 32 | +- Layer 1: Schema generation |
| 33 | + - `eng/build/SchemaGenerator.cs` |
| 34 | +- Layer 2: Roslyn source generator |
| 35 | + - `src/qyl.servicedefaults.generator/` |
| 36 | +- Layer 3: Runtime instrumentation |
| 37 | + - `src/qyl.servicedefaults/` |
| 38 | + - `src/qyl.collector/` |
| 39 | + |
| 40 | +Layers must never be conflated. |
| 41 | + |
| 42 | +## Agent roles |
| 43 | + |
| 44 | +`agent-collector` |
| 45 | +- Role: OTLP collector maintainer |
| 46 | +- Scope: `src/qyl.collector` |
| 47 | +- Focus: OTLP ingestion, DuckDB batching, SSE streaming lifecycle |
| 48 | +- Rules: minimal diffs; never modify generators or schema code |
| 49 | +- Default model: Claude Sonnet 4.6 (`high`) |
| 50 | + |
| 51 | +`agent-generator` |
| 52 | +- Role: Roslyn pipeline maintainer |
| 53 | +- Scope: `src/qyl.servicedefaults.generator` |
| 54 | +- Focus: incremental pipeline behavior, generator correctness, compile-time interception design |
| 55 | +- Rules: preserve pipeline isolation/design; never touch `SchemaGenerator.cs` unless explicitly required |
| 56 | +- Default model: Claude Opus 4.6 (`max`) |
| 57 | + |
| 58 | +`agent-schema` |
| 59 | +- Role: schema generator maintainer |
| 60 | +- Scope: `eng/build/SchemaGenerator.cs` |
| 61 | +- Focus: OpenAPI to C# scalar/enum generation, DuckDB DDL emission |
| 62 | +- Rules: preserve single-source-of-truth schema pipeline |
| 63 | +- Default model: Claude Opus 4.6 (`max`) |
| 64 | + |
| 65 | +`agent-investigator` |
| 66 | +- Role: cross-layer debugging agent |
| 67 | +- Allowed scope: generator, runtime, collector |
| 68 | +- Tasks: incident investigation, telemetry tracing, root cause analysis, cross-layer fault isolation |
| 69 | +- Default model: Claude Opus 4.6 (`max`) |
| 70 | + |
| 71 | +`agent-architect` |
| 72 | +- Role: repository architecture engineer |
| 73 | +- Tasks: cross-module cleanup, architecture improvements, multi-file changes, cross-cutting concern reduction |
| 74 | +- Default model: Claude Opus 4.6 (`max`) |
| 75 | + |
| 76 | +`agent-gardener` |
| 77 | +- Role: repository gardener |
| 78 | +- Tasks: small correctness fixes, comment cleanup, verification improvements, documentation accuracy |
| 79 | +- Default model: Claude Sonnet 4.6 (`high`) |
| 80 | + |
| 81 | +## Required generated files per worktree |
| 82 | + |
| 83 | +Create/refresh: |
| 84 | +- `AGENTS.md` |
| 85 | +- `CLAUDE.md` |
| 86 | +- `.codex/agent.md` |
| 87 | + |
| 88 | +`CLAUDE.md` content: |
| 89 | +```text |
| 90 | +Read and follow AGENTS.md in this worktree. |
| 91 | +``` |
| 92 | + |
| 93 | +`.codex/agent.md` content: |
| 94 | +```text |
| 95 | +Read and follow AGENTS.md in this worktree. |
| 96 | +``` |
| 97 | + |
| 98 | +## Script: `scripts/setup-worktrees.sh` |
| 99 | + |
| 100 | +```bash |
| 101 | +#!/usr/bin/env bash |
| 102 | +set -euo pipefail |
| 103 | + |
| 104 | +repo_root="$(git rev-parse --show-toplevel)" |
| 105 | +worktrees_dir="$repo_root/.worktrees" |
| 106 | + |
| 107 | +worktrees=( |
| 108 | + agent-collector |
| 109 | + agent-generator |
| 110 | + agent-schema |
| 111 | + agent-investigator |
| 112 | + agent-architect |
| 113 | + agent-gardener |
| 114 | +) |
| 115 | + |
| 116 | +role_contents_common='\ |
| 117 | +## Repository architecture constraints |
| 118 | +
|
| 119 | +Layer 1: Schema generation |
| 120 | +- `eng/build/SchemaGenerator.cs` |
| 121 | +- NUKE build emits models, enums, scalars, and DuckDB DDL |
| 122 | +
|
| 123 | +Layer 2: Roslyn source generator |
| 124 | +- `src/qyl.servicedefaults.generator/` |
| 125 | +- 7 incremental pipelines that emit compile-time interceptors |
| 126 | +
|
| 127 | +Layer 3: Runtime instrumentation |
| 128 | +- `src/qyl.servicedefaults/` |
| 129 | +- `src/qyl.collector/` |
| 130 | +
|
| 131 | +Layers must never be conflated. |
| 132 | +' |
| 133 | + |
| 134 | +get_agent_section() { |
| 135 | + local role=$1 |
| 136 | + |
| 137 | + case "$role" in |
| 138 | + agent-collector) |
| 139 | + cat <<'EOF_AGENT' |
| 140 | +## Role |
| 141 | +- OTLP collector maintainer |
| 142 | +- Scope: `src/qyl.collector` |
| 143 | +- Focus: OTLP ingestion, DuckDB batching, SSE streaming lifecycle |
| 144 | +- Rules: minimal diffs; never modify generators or schema code |
| 145 | +- Default model: Claude Sonnet 4.6 (effort: high) |
| 146 | +- Escalate to Opus max only for cross-layer investigation. |
| 147 | +EOF_AGENT |
| 148 | + ;; |
| 149 | + agent-generator) |
| 150 | + cat <<'EOF_AGENT' |
| 151 | +## Role |
| 152 | +- Roslyn pipeline maintainer |
| 153 | +- Scope: `src/qyl.servicedefaults.generator` |
| 154 | +- Focus: incremental pipeline behavior, generator correctness, compile-time interception design |
| 155 | +- Rules: maintain pipeline isolation; preserve incremental generator design; never touch `SchemaGenerator.cs` unless explicitly required |
| 156 | +- Default model: Claude Opus 4.6 (effort: max) |
| 157 | +EOF_AGENT |
| 158 | + ;; |
| 159 | + agent-schema) |
| 160 | + cat <<'EOF_AGENT' |
| 161 | +## Role |
| 162 | +- schema generator maintainer |
| 163 | +- Scope: `eng/build/SchemaGenerator.cs` |
| 164 | +- Focus: OpenAPI to C# scalar/enum generation and DuckDB DDL emission |
| 165 | +- Rules: maintain single-source-of-truth schema pipeline |
| 166 | +- Default model: Claude Opus 4.6 (effort: max) |
| 167 | +EOF_AGENT |
| 168 | + ;; |
| 169 | + agent-investigator) |
| 170 | + cat <<'EOF_AGENT' |
| 171 | +## Role |
| 172 | +- cross-layer debugging agent |
| 173 | +- Allowed scope: generator, runtime, collector |
| 174 | +- Tasks: incident investigation, telemetry tracing, root cause analysis, cross-layer fault isolation |
| 175 | +- Default model: Claude Opus 4.6 (effort: max) |
| 176 | +EOF_AGENT |
| 177 | + ;; |
| 178 | + agent-architect) |
| 179 | + cat <<'EOF_AGENT' |
| 180 | +## Role |
| 181 | +- repository architecture engineer |
| 182 | +- Tasks: cross-module cleanup, architecture improvements, multi-file changes, cross-cutting concern reduction |
| 183 | +- Default model: Claude Opus 4.6 (effort: max) |
| 184 | +EOF_AGENT |
| 185 | + ;; |
| 186 | + agent-gardener) |
| 187 | + cat <<'EOF_AGENT' |
| 188 | +## Role |
| 189 | +- repository gardener |
| 190 | +- Tasks: small correctness fixes, comment cleanup, verification improvements, documentation accuracy |
| 191 | +- Default model: Claude Sonnet 4.6 (effort: high) |
| 192 | +EOF_AGENT |
| 193 | + ;; |
| 194 | + *) |
| 195 | + echo "Unknown role: $role" >&2 |
| 196 | + exit 1 |
| 197 | + ;; |
| 198 | + esac |
| 199 | +} |
| 200 | + |
| 201 | +write_common_files() { |
| 202 | + local path=$1 |
| 203 | + local role=$2 |
| 204 | + |
| 205 | + cat > "$path/AGENTS.md" <<EOF_AGENTS |
| 206 | +# AGENTS.md for $role |
| 207 | +
|
| 208 | +${role_contents_common} |
| 209 | +
|
| 210 | +$(get_agent_section "$role") |
| 211 | +
|
| 212 | +## Required verification |
| 213 | +- dotnet build |
| 214 | +- dotnet test |
| 215 | +EOF_AGENTS |
| 216 | + |
| 217 | + mkdir -p "$path/.codex" |
| 218 | + printf 'Read and follow AGENTS.md in this worktree.\n' > "$path/CLAUDE.md" |
| 219 | + printf 'Read and follow AGENTS.md in this worktree.\n' > "$path/.codex/agent.md" |
| 220 | +} |
| 221 | + |
| 222 | +is_registered_worktree() { |
| 223 | + local target=$1 |
| 224 | + git worktree list --porcelain | awk -v target="$target" ' |
| 225 | + $1 == "worktree" {path = $2} |
| 226 | + path == target {found = 1} |
| 227 | + END {exit found ? 0 : 1} |
| 228 | + ' |
| 229 | +} |
| 230 | + |
| 231 | +mkdir -p "$worktrees_dir" |
| 232 | +declare -A status |
| 233 | + |
| 234 | +for name in "${worktrees[@]}"; do |
| 235 | + path="$worktrees_dir/$name" |
| 236 | + branch="worktree/$name" |
| 237 | + |
| 238 | + if [[ -e "$path" ]]; then |
| 239 | + if ! is_registered_worktree "$path"; then |
| 240 | + echo "ERROR: path exists but is not a registered worktree: $path" >&2 |
| 241 | + exit 1 |
| 242 | + fi |
| 243 | + |
| 244 | + existing_branch=$(git -C "$path" rev-parse --abbrev-ref HEAD) |
| 245 | + if [[ "$existing_branch" != "$branch" ]]; then |
| 246 | + echo "ERROR: path $path uses branch '$existing_branch', expected '$branch'" >&2 |
| 247 | + exit 1 |
| 248 | + fi |
| 249 | + |
| 250 | + write_common_files "$path" "$name" |
| 251 | + status["$name"]=reused |
| 252 | + continue |
| 253 | + fi |
| 254 | + |
| 255 | + if ! git show-ref --verify --quiet "refs/heads/$branch"; then |
| 256 | + git branch "$branch" HEAD |
| 257 | + fi |
| 258 | + |
| 259 | + if git worktree list --porcelain | awk -v branch="refs/heads/$branch" ' |
| 260 | + $1 == "branch" && $2 == branch {found = 1} |
| 261 | + END {exit found ? 0 : 1} |
| 262 | + '; then |
| 263 | + echo "ERROR: branch '$branch' already used by another worktree" >&2 |
| 264 | + exit 1 |
| 265 | + fi |
| 266 | + |
| 267 | + git worktree add "$path" "$branch" |
| 268 | + write_common_files "$path" "$name" |
| 269 | + status["$name"]=created |
| 270 | + |
| 271 | +done |
| 272 | + |
| 273 | +echo "--- Worktree summary ---" |
| 274 | +for name in "${worktrees[@]}"; do |
| 275 | + path="$worktrees_dir/$name" |
| 276 | + b=$(git -C "$path" rev-parse --abbrev-ref HEAD) |
| 277 | + printf "%s | %s | %s\n" "$name" "${status[$name]}" "$path" |
| 278 | + printf " AGENTS.md: %s\n" "$( [[ -f \"$path/AGENTS.md\" ]] && echo exists || echo missing )" |
| 279 | + printf " CLAUDE.md: %s\n" "$( [[ -f \"$path/CLAUDE.md\" ]] && echo exists || echo missing )" |
| 280 | + printf " .codex/agent.md: %s\n" "$( [[ -f \"$path/.codex/agent.md\" ]] && echo exists || echo missing )" |
| 281 | + echo " branch: $b" |
| 282 | +done |
| 283 | + |
| 284 | +printf "\nGit worktree list (worktrees only):\n" |
| 285 | +git worktree list --porcelain | awk '$1 == "worktree" {print $2}' |
0 commit comments