paircode 0.12.1__tar.gz → 0.12.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. {paircode-0.12.1 → paircode-0.12.4}/PKG-INFO +1 -1
  2. {paircode-0.12.1 → paircode-0.12.4}/pyproject.toml +1 -1
  3. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/__init__.py +1 -1
  4. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/peerlab.py +61 -7
  5. paircode-0.12.4/src/paircode/templates/claude/commands/peerlab.md +113 -0
  6. {paircode-0.12.1 → paircode-0.12.4}/tests/test_peerlab.py +77 -0
  7. paircode-0.12.1/src/paircode/templates/claude/commands/peerlab.md +0 -71
  8. {paircode-0.12.1 → paircode-0.12.4}/.gitignore +0 -0
  9. {paircode-0.12.1 → paircode-0.12.4}/LICENSE +0 -0
  10. {paircode-0.12.1 → paircode-0.12.4}/README.md +0 -0
  11. {paircode-0.12.1 → paircode-0.12.4}/diary/001-step-a-architecture.md +0 -0
  12. {paircode-0.12.1 → paircode-0.12.4}/diary/002-v0.10-release-pipeline.md +0 -0
  13. {paircode-0.12.1 → paircode-0.12.4}/diary/003-arch-b-pivot-grappling.md +0 -0
  14. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/__main__.py +0 -0
  15. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/cli.py +0 -0
  16. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/converge.py +0 -0
  17. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/detect.py +0 -0
  18. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/handshake.py +0 -0
  19. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/installer.py +0 -0
  20. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/runner.py +0 -0
  21. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/state.py +0 -0
  22. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/templates/FOCUS.md +0 -0
  23. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/templates/JOURNEY.md +0 -0
  24. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/templates/claude/commands/paircode.md +0 -0
  25. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/templates/codex/commands/paircode.md +0 -0
  26. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/templates/gemini/commands/paircode.toml +0 -0
  27. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/templates/peers.yaml +0 -0
  28. {paircode-0.12.1 → paircode-0.12.4}/src/paircode/util.py +0 -0
  29. {paircode-0.12.1 → paircode-0.12.4}/tests/__init__.py +0 -0
  30. {paircode-0.12.1 → paircode-0.12.4}/tests/test_cli_smoke.py +0 -0
  31. {paircode-0.12.1 → paircode-0.12.4}/tests/test_converge.py +0 -0
  32. {paircode-0.12.1 → paircode-0.12.4}/tests/test_smoke.py +0 -0
  33. {paircode-0.12.1 → paircode-0.12.4}/tests/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paircode
3
- Version: 0.12.1
3
+ Version: 0.12.4
4
4
  Summary: Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review.
5
5
  Project-URL: Homepage, https://github.com/starshipagentic/paircode
6
6
  Project-URL: Repository, https://github.com/starshipagentic/paircode
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "paircode"
7
- version = "0.12.1"
7
+ version = "0.12.4"
8
8
  description = "Adversarial journey framework — orchestrate multiple LLM peers through research, plan, and execute stages with file-trace peer review."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -2,4 +2,4 @@
2
2
 
3
3
  See README.md and diary/001-step-a-architecture.md for the full design.
4
4
  """
5
- __version__ = "0.12.0"
5
+ __version__ = "0.12.3"
@@ -41,6 +41,30 @@ SEED_EXCLUDES: tuple[str, ...] = (
41
41
 
42
42
  GITIGNORE_BLOCK_HEADER = "# peerlab — per-peer independent parallel labs"
43
43
 
44
+ # Defaults dropped into each new peer lab's own .gitignore so peer commits
45
+ # don't accidentally include Python bytecode, editor junk, OS cruft.
46
+ # If the lab inherited a .gitignore from the project root (rsync seed), we
47
+ # APPEND this block — user's rules win, these are a safety net.
48
+ LAB_GITIGNORE_BLOCK = """\
49
+ # peerlab defaults — python + editor junk (keep peer commits clean)
50
+ __pycache__/
51
+ *.py[cod]
52
+ *$py.class
53
+ *.egg-info/
54
+ .pytest_cache/
55
+ .mypy_cache/
56
+ .ruff_cache/
57
+ .tox/
58
+ .coverage
59
+ .coverage.*
60
+ htmlcov/
61
+ .DS_Store
62
+ .idea/
63
+ .vscode/
64
+ *.swp
65
+ """
66
+ LAB_GITIGNORE_MARKER = "peerlab defaults"
67
+
44
68
 
45
69
  @dataclass(frozen=True)
46
70
  class EnsureResult:
@@ -108,6 +132,25 @@ def _rsync_project(src: Path, dst: Path, excludes: Iterable[str] = SEED_EXCLUDES
108
132
  shutil.copytree(src, dst, ignore=_ignore)
109
133
 
110
134
 
135
+ def ensure_lab_gitignore(lab: Path) -> bool:
136
+ """Ensure the peer lab has Python/editor junk gitignored. Idempotent.
137
+
138
+ If the lab already has a `.gitignore` (inherited via rsync from the
139
+ project root), our block is appended so user rules win. If none exists
140
+ (greenfield), our block IS the .gitignore.
141
+
142
+ Returns True if the block was added this call, False if it was already
143
+ there (marker detection).
144
+ """
145
+ gitignore = lab / ".gitignore"
146
+ existing = gitignore.read_text(encoding="utf-8") if gitignore.exists() else ""
147
+ if LAB_GITIGNORE_MARKER in existing:
148
+ return False
149
+ new_text = (existing.rstrip() + "\n\n" if existing.strip() else "") + LAB_GITIGNORE_BLOCK
150
+ gitignore.write_text(new_text, encoding="utf-8")
151
+ return True
152
+
153
+
111
154
  def _git_init(lab: Path) -> None:
112
155
  """`git init` inside `lab/`. No-op if already inited."""
113
156
  if (lab / ".git").exists():
@@ -121,17 +164,24 @@ def _git_init(lab: Path) -> None:
121
164
  subprocess.run(["git", "init", "--quiet", str(lab)], check=True)
122
165
 
123
166
 
124
- def _git_initial_commit(lab: Path, message: str = "initial seed from project root") -> bool:
167
+ def _git_initial_commit(
168
+ lab: Path, peer_id: str, message: str = "initial seed from project root"
169
+ ) -> bool:
125
170
  """Stage everything + commit. Returns True if a commit was made.
126
171
 
127
- Sets local-only user.name/email (doesn't touch global git config) so the
128
- commit is authored even on machines without a global identity.
172
+ Authors the commit as the peer itself (`{peer_id} <{peer_id}@peerlab.local>`)
173
+ via lab-local git config. Every subsequent peer commit inside this lab
174
+ inherits that local identity — so `git log` in a peer's lab attributes
175
+ all work to that peer, clean replay.
129
176
  """
130
177
  if not any(lab.iterdir()):
131
178
  return False
132
- # Local git config — scoped to this repo
133
- subprocess.run(["git", "-C", str(lab), "config", "user.name", "paircode-peerlab"], check=False)
134
- subprocess.run(["git", "-C", str(lab), "config", "user.email", "peerlab@paircode.local"], check=False)
179
+ # Local git config — scoped to this lab, doesn't touch global identity.
180
+ subprocess.run(["git", "-C", str(lab), "config", "user.name", peer_id], check=False)
181
+ subprocess.run(
182
+ ["git", "-C", str(lab), "config", "user.email", f"{peer_id}@peerlab.local"],
183
+ check=False,
184
+ )
135
185
  subprocess.run(["git", "-C", str(lab), "add", "-A"], check=False)
136
186
  # Commit only if there's something staged
137
187
  diff_check = subprocess.run(
@@ -183,8 +233,12 @@ def ensure_peer_labs(state: PaircodeState) -> list[EnsureResult]:
183
233
  lab.mkdir(exist_ok=True)
184
234
  # Seed from project root (might be empty for greenfield — rsync handles fine)
185
235
  _rsync_project(project_root, lab)
236
+ # Drop peerlab-default .gitignore so peer commits don't pull in
237
+ # __pycache__, .pytest_cache, editor junk etc. Appends if the lab
238
+ # already inherited a .gitignore from the project root.
239
+ ensure_lab_gitignore(lab)
186
240
  _git_init(lab)
187
- _git_initial_commit(lab, message="initial seed from project root")
241
+ _git_initial_commit(lab, pid, message="initial seed from project root")
188
242
  results.append(EnsureResult(peer_id=pid, lab_path=lab, status="created"))
189
243
  return results
190
244
 
@@ -0,0 +1,113 @@
1
+ ---
2
+ description: peerlab — each peer owns a parallel lab with its own git. Peers write real code, then cross-review each other's labs, then team lead synthesizes.
3
+ ---
4
+
5
+ You are the team lead for a `/peerlab` run. Each peer has its own persistent lab at `.peerlab/<peer-id>/` with its own `.git/`. Peers **own** their labs. The flow is:
6
+
7
+ 1. **Work round** — every peer builds in its own lab.
8
+ 2. **Cross-review round** — every peer reads the OTHER peers' labs and writes an honest critique (`CRITIQUES.md`) in its own lab. Peer-reviews-peer is the adversarial content; do NOT skip this.
9
+ 3. **Team-lead synthesis** — alpha reads both the code diffs AND each peer's critiques, delivers the final take.
10
+
11
+ Separate concept from `/paircode` — no focus dirs, no stages, no consensus.md. Real code evolution + peer critiques on disk, synthesis in the chat.
12
+
13
+ `$ARGUMENTS` is the user's raw prompt. Pass it through verbatim.
14
+
15
+ ## Step 1 — Bootstrap (silent via Bash)
16
+
17
+ ```bash
18
+ paircode peerlab ensure # scaffolds + seeds + git-inits each lab (idempotent)
19
+ PEERS=$(paircode roster --alpha claude)
20
+ ```
21
+
22
+ `paircode peerlab ensure` is safe to run every time. First time: copies the project root into each peer's lab (minus standard excludes) and `git init`s it. Subsequent times: no-op.
23
+
24
+ ## Step 2 — Work round (fire every peer in parallel)
25
+
26
+ For each peer-id in `$PEERS`, spawn one subagent via the Agent tool (`subagent_type=general-purpose`, `run_in_background=true`) — all in a single message so they run concurrently.
27
+
28
+ Each subagent's prompt:
29
+
30
+ ```
31
+ Monitor peer {peer-id} on the work round. Run this shell command and wait:
32
+
33
+ paircode peerlab invoke {peer-id} "<the user's prompt verbatim>"
34
+
35
+ That fires the peer with cwd = .peerlab/{peer-id}/ (its own lab). The peer
36
+ commits its work in its own .git before finishing.
37
+
38
+ After it returns, gather:
39
+ 1. The peer's stdout (narrative).
40
+ 2. git -C .peerlab/{peer-id} log --oneline -5
41
+ 3. git -C .peerlab/{peer-id} diff HEAD~1 HEAD --stat
42
+
43
+ Report back under 400 words: peer-id, ok=yes/no, duration, 1-line summary,
44
+ and the git stat.
45
+ ```
46
+
47
+ Wait for every peer to finish the work round before starting Step 3.
48
+
49
+ ## Step 3 — Cross-review round (peers read each other's labs)
50
+
51
+ This is the adversarial heart of `/peerlab`. Every peer now reads every OTHER peer's lab and writes an honest critique. Do not skip this — it's what makes `/peerlab` different from "parallel output".
52
+
53
+ If only ONE peer finished Step 2 successfully, skip Step 3 (nobody to cross-review) and note it in the final synthesis. Otherwise:
54
+
55
+ For each peer-id in the set of peers that succeeded in Step 2, spawn one subagent (Agent tool, `run_in_background=true`) — all in one message. Each subagent's prompt:
56
+
57
+ ```
58
+ Monitor peer {peer-id} on the cross-review round. Run this shell command
59
+ and wait:
60
+
61
+ paircode peerlab invoke {peer-id} "You already committed your own work
62
+ in your lab — your cwd. Now cross-review every OTHER peer's lab.
63
+ Siblings live at ../<other-peer-id>/ relative to your cwd. List them
64
+ with `ls ..` and exclude your own id ({peer-id}).
65
+
66
+ For each other peer:
67
+ git -C ../<other-peer-id> log --oneline
68
+ git -C ../<other-peer-id> diff HEAD~1 HEAD
69
+ # read the changed files directly if the diff isn't enough
70
+
71
+ Write CRITIQUES.md in YOUR cwd (your own lab) with one section per
72
+ peer you reviewed. Severity-ranked findings with file:line citations
73
+ pointing at the OTHER peer's files. Be specific. Don't pile on,
74
+ don't rubber-stamp. Cite real lines, not vibes.
75
+
76
+ Then commit your critique:
77
+ git add CRITIQUES.md
78
+ git commit -m 'cross-review'"
79
+
80
+ After it returns, verify:
81
+ - .peerlab/{peer-id}/CRITIQUES.md exists and has non-trivial content
82
+ - git -C .peerlab/{peer-id} log -1 --oneline mentions 'cross-review'
83
+
84
+ Report back under 300 words: peer-id, ok=yes/no, duration, and a 1-line
85
+ summary of whose code this peer found most/least convincing.
86
+ ```
87
+
88
+ Wait for every peer to finish cross-review before Step 4.
89
+
90
+ ## Step 4 — Synthesize (one message to user, ≤300 words)
91
+
92
+ For each peer, read BOTH:
93
+ - The work diff: `git -C .peerlab/<peer-id> log -p HEAD~2..HEAD` (covers work commit + critique commit — 2 commits if both rounds ran, 1 if Step 3 was skipped).
94
+ - The critique: `.peerlab/<peer-id>/CRITIQUES.md` — what THIS peer thought of the OTHERS.
95
+
96
+ Then produce a tight team-lead synthesis that leans on the peers' own voices, not just yours:
97
+
98
+ - **Per-peer implementation summary**: 1–2 lines each — files touched, LOC delta, the shape of the approach.
99
+ - **What peers said about each other**: extract the sharpest findings from each `CRITIQUES.md`. If codex called out a bug in gemini's lab with a file:line, quote it. If gemini pointed at an edge case codex missed, quote it. These are the adversarial moments — don't bury them.
100
+ - **Cross-cutting themes**: where did peers agree about what's wrong? Where did they disagree? Disagreement is signal.
101
+ - **Honest head-to-head**: which lab made the cleanest move, informed by the peer critiques? Don't play favorites, don't hedge.
102
+ - **What to pull into alpha**: if a peer's approach is objectively better, name files + approach. Don't do the pull yourself — recommend.
103
+
104
+ No focus dirs. No consensus.md. Just the synthesis as your chat reply.
105
+
106
+ ## Guardrails
107
+
108
+ - **Alpha (this session) does NOT edit `.peerlab/<peer-id>/`.** Peers own their labs. You only read.
109
+ - **Alpha does NOT implement the user's prompt in the project root.** That's a `/paircode` job or a direct Claude Code ask. `/peerlab` is strictly "peers go build + cross-review; I read the diffs and critiques."
110
+ - **Fire every peer** unless the user's prompt explicitly names `--peer`/`--peers`. Watchdog protects against hangs.
111
+ - **Do not skip Step 3.** The cross-review round is the point. If only one peer made it through Step 2, note why in the synthesis and skip Step 3 explicitly.
112
+ - **If a peer fails** (hung, errored, no commits), note it in the synthesis and continue. One flaky peer doesn't sink the run.
113
+ - **No AI attribution** in anything you write — see the maintainer's CLAUDE.md.
@@ -11,8 +11,10 @@ from click.testing import CliRunner
11
11
  from paircode.cli import main
12
12
  from paircode.handshake import ProposedPeer
13
13
  from paircode.peerlab import (
14
+ LAB_GITIGNORE_MARKER,
14
15
  PEERLAB_DIRNAME,
15
16
  ensure_gitignore,
17
+ ensure_lab_gitignore,
16
18
  ensure_peer_labs,
17
19
  peer_lab_path,
18
20
  )
@@ -176,6 +178,81 @@ def test_paircode_peerlab_help_is_reachable():
176
178
  # Seed excludes honored
177
179
  # ---------------------------------------------------------------------------
178
180
 
181
+ def test_lab_gitignore_has_pycache_on_fresh_lab(tmp_path, monkeypatch):
182
+ """Fresh lab's .gitignore excludes __pycache__ and bytecode so peer
183
+ commits don't pull in build junk. Regression for v0.12.0 bug where
184
+ `git add -A` in peer labs committed .pyc files."""
185
+ _init_and_scaffold(tmp_path, monkeypatch)
186
+ state = find_paircode()
187
+ ensure_peer_labs(state)
188
+ lab = peer_lab_path(state, "peer-a-codex")
189
+ gitignore = lab / ".gitignore"
190
+ assert gitignore.exists()
191
+ content = gitignore.read_text()
192
+ assert "__pycache__/" in content
193
+ assert "*.py[cod]" in content
194
+ assert LAB_GITIGNORE_MARKER in content
195
+
196
+
197
+ def test_ensure_lab_gitignore_appends_to_existing(tmp_path):
198
+ """If user's project already has a .gitignore (rsync brought it over),
199
+ the peerlab block is APPENDED — user rules preserved."""
200
+ lab = tmp_path
201
+ (lab / ".gitignore").write_text("# user project rules\nsecrets.env\n")
202
+ assert ensure_lab_gitignore(lab) is True
203
+ content = (lab / ".gitignore").read_text()
204
+ assert "secrets.env" in content # user rule preserved
205
+ assert LAB_GITIGNORE_MARKER in content # our block added
206
+
207
+
208
+ def test_ensure_lab_gitignore_is_idempotent(tmp_path):
209
+ """Second call no-ops if marker already there."""
210
+ lab = tmp_path
211
+ assert ensure_lab_gitignore(lab) is True
212
+ before = (lab / ".gitignore").read_text()
213
+ assert ensure_lab_gitignore(lab) is False
214
+ assert (lab / ".gitignore").read_text() == before
215
+
216
+
217
+ def test_initial_commit_authored_as_peer_id(tmp_path, monkeypatch):
218
+ """Initial seed commit must be authored as the peer_id so replay
219
+ (git log) attributes work to each peer. Fixes v0.12.0 where commits
220
+ were authored as generic 'paircode-peerlab'."""
221
+ _init_and_scaffold(tmp_path, monkeypatch)
222
+ state = find_paircode()
223
+ ensure_peer_labs(state)
224
+ for pid in ("peer-a-codex", "peer-b-gemini"):
225
+ lab = peer_lab_path(state, pid)
226
+ r = subprocess.run(
227
+ ["git", "-C", str(lab), "log", "-1", "--format=%an <%ae>"],
228
+ capture_output=True, text=True, check=False,
229
+ )
230
+ assert pid in r.stdout, f"commit author should contain {pid}; got {r.stdout}"
231
+ assert f"{pid}@peerlab.local" in r.stdout
232
+
233
+
234
+ def test_different_peers_produce_different_initial_commits(tmp_path, monkeypatch):
235
+ """Distinct peer_ids as commit authors mean the seed commit SHAs differ
236
+ across labs — proves independence beyond 'same content, same author,
237
+ accidental SHA collision'."""
238
+ _init_and_scaffold(tmp_path, monkeypatch)
239
+ state = find_paircode()
240
+ ensure_peer_labs(state)
241
+ codex_lab = peer_lab_path(state, "peer-a-codex")
242
+ gemini_lab = peer_lab_path(state, "peer-b-gemini")
243
+
244
+ def head_sha(lab):
245
+ r = subprocess.run(
246
+ ["git", "-C", str(lab), "rev-parse", "HEAD"],
247
+ capture_output=True, text=True, check=False,
248
+ )
249
+ return r.stdout.strip()
250
+
251
+ assert head_sha(codex_lab) != head_sha(gemini_lab), (
252
+ "initial seed commits should differ across labs since authors differ"
253
+ )
254
+
255
+
179
256
  def test_seed_excludes_dot_paircode_and_dot_git(tmp_path, monkeypatch):
180
257
  """Seeded labs must not contain `.paircode/` (recursion) or the outer
181
258
  repo's `.git/` (each lab gets its own fresh git instead)."""
@@ -1,71 +0,0 @@
1
- ---
2
- description: peerlab — each peer does real work in its own parallel lab with its own git; team lead reads the diffs and synthesizes.
3
- ---
4
-
5
- You are the team lead for a `/peerlab` run. Each peer has its own persistent lab at `.peerlab/<peer-id>/` with its own `.git/`. Peers **own** their labs — they write code, run tests, commit as they go. Your job: fire every peer on the user's prompt, read what they actually did (the diffs), and synthesize.
6
-
7
- This is a separate concept from `/paircode` — no focus dirs, no stages, no markdown consensus files. The deliverable is real code evolution per lab + your synthesis at the end.
8
-
9
- `$ARGUMENTS` is the user's raw prompt. Pass it through verbatim.
10
-
11
- ## Step 1 — Bootstrap (silent via Bash)
12
-
13
- ```bash
14
- paircode peerlab ensure # scaffolds + seeds + git-inits each lab (idempotent)
15
- PEERS=$(paircode roster --alpha claude)
16
- ```
17
-
18
- `paircode peerlab ensure` is safe to run every time. First time: copies the project root into each peer's lab (minus standard excludes) and `git init`s it. Subsequent times: no-op.
19
-
20
- ## Step 2 — Fire every peer in parallel
21
-
22
- For each peer-id in `$PEERS`, spawn one subagent via the Agent tool (`subagent_type=general-purpose`, `run_in_background=true`) — all in a single message so they run concurrently.
23
-
24
- Each subagent's prompt:
25
-
26
- ```
27
- You are monitoring the {peer-id} peer. Run this shell command and wait:
28
-
29
- paircode peerlab invoke {peer-id} "<the user's prompt verbatim>"
30
-
31
- That fires the peer with its cwd set to .peerlab/{peer-id}/ (its own lab).
32
- The peer is expected to commit its work in its own .git before finishing.
33
-
34
- After it returns, gather:
35
- 1. The peer's stdout (its narrative of what it did).
36
- 2. `git -C .peerlab/{peer-id} log --oneline -5` — recent commits.
37
- 3. `git -C .peerlab/{peer-id} diff HEAD~1 HEAD --stat` — what changed.
38
-
39
- Report back to team lead in under 400 words: peer-id, ok=yes/no, duration,
40
- 1-line summary of what the peer claims it did, plus the git stat output.
41
- ```
42
-
43
- ## Step 3 — Read the actual code each peer wrote
44
-
45
- When every peer has reported, for each peer-id:
46
-
47
- ```bash
48
- git -C .peerlab/<peer-id> log --oneline -5
49
- git -C .peerlab/<peer-id> diff HEAD~1 HEAD
50
- ```
51
-
52
- Read the actual diffs (use the Read tool on the changed files inside each lab, or just the full `git diff` output). This is the real content — NOT the peer's narrative in stdout. Trust the code over the claims.
53
-
54
- ## Step 4 — Synthesize (one message to user, ≤300 words)
55
-
56
- Produce a tight team-lead synthesis:
57
-
58
- - **Per-peer summary**: 1–2 lines each — files touched, LOC delta, the shape of the approach.
59
- - **Cross-cutting themes**: did multiple peers hit the same issue/solution? That's signal.
60
- - **Honest head-to-head**: which lab made the cleanest move? Which went off-track? Don't play favorites but don't hedge.
61
- - **What to pull into alpha**: if any peer's approach is objectively better than what alpha would do, name the files + the approach. Don't do the pull yourself — just recommend.
62
-
63
- No focus dirs. No consensus.md. No markdown artifacts on disk. Just the synthesis as your reply to the user.
64
-
65
- ## Guardrails
66
-
67
- - **Alpha (this session) does NOT edit `.peerlab/<peer-id>/`.** Peers own their labs. You only read.
68
- - **Alpha does NOT implement the user's prompt in the project root.** That's a `/paircode` job or a direct Claude Code ask. `/peerlab` is strictly "peers go build it, I read the diffs."
69
- - **Fire every peer** unless the user's prompt explicitly names `--peer`/`--peers`. Watchdog protects against hangs.
70
- - **If a peer fails** (hung, errored, no commits), note it in the synthesis and continue. One flaky peer doesn't sink the run.
71
- - **No AI attribution** in anything you write — see the maintainer's CLAUDE.md.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes