ensemble-claude 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- ensemble/__init__.py +1 -1
- ensemble/cli.py +2 -0
- ensemble/commands/_issue_impl.py +150 -0
- ensemble/commands/_launch_impl.py +172 -47
- ensemble/commands/issue.py +36 -0
- ensemble/git_utils.py +157 -0
- ensemble/issue_provider.py +97 -0
- ensemble/providers/__init__.py +5 -0
- ensemble/providers/github.py +103 -0
- ensemble/templates/agents/conductor.md +64 -10
- ensemble/templates/agents/dispatch.md +128 -14
- ensemble/templates/agents/worker.md +55 -0
- ensemble/templates/commands/go-issue.md +157 -0
- ensemble/templates/commands/go.md +19 -4
- ensemble/templates/scripts/launch.sh +100 -57
- ensemble/templates/scripts/pane-setup.sh +103 -30
- {ensemble_claude-0.3.0.dist-info → ensemble_claude-0.4.0.dist-info}/METADATA +8 -5
- {ensemble_claude-0.3.0.dist-info → ensemble_claude-0.4.0.dist-info}/RECORD +21 -14
- {ensemble_claude-0.3.0.dist-info → ensemble_claude-0.4.0.dist-info}/WHEEL +0 -0
- {ensemble_claude-0.3.0.dist-info → ensemble_claude-0.4.0.dist-info}/entry_points.txt +0 -0
- {ensemble_claude-0.3.0.dist-info → ensemble_claude-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Abstract base class for issue providers (GitHub, GitLab, etc.)."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Issue:
|
|
10
|
+
"""Represents an issue from a code hosting platform."""
|
|
11
|
+
|
|
12
|
+
number: int
|
|
13
|
+
title: str
|
|
14
|
+
body: str
|
|
15
|
+
url: str
|
|
16
|
+
state: str
|
|
17
|
+
labels: list[str]
|
|
18
|
+
|
|
19
|
+
def branch_slug(self, max_length: int = 50) -> str:
|
|
20
|
+
"""Generate a git branch name slug from the issue.
|
|
21
|
+
|
|
22
|
+
Format: issue/<number>-<slugified-title>
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
max_length: Maximum length of the slug portion (excluding prefix).
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
A valid git branch name.
|
|
29
|
+
"""
|
|
30
|
+
# Convert title to lowercase
|
|
31
|
+
slug = self.title.lower()
|
|
32
|
+
|
|
33
|
+
# Replace special characters with spaces
|
|
34
|
+
slug = re.sub(r"[^a-z0-9\s-]", "", slug)
|
|
35
|
+
|
|
36
|
+
# Replace whitespace with hyphens
|
|
37
|
+
slug = re.sub(r"\s+", "-", slug)
|
|
38
|
+
|
|
39
|
+
# Remove consecutive hyphens
|
|
40
|
+
slug = re.sub(r"-+", "-", slug)
|
|
41
|
+
|
|
42
|
+
# Trim hyphens from ends
|
|
43
|
+
slug = slug.strip("-")
|
|
44
|
+
|
|
45
|
+
# Truncate to max length
|
|
46
|
+
if len(slug) > max_length:
|
|
47
|
+
# Try to cut at a word boundary
|
|
48
|
+
slug = slug[:max_length].rsplit("-", 1)[0]
|
|
49
|
+
|
|
50
|
+
return f"issue/{self.number}-{slug}"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class IssueProvider(ABC):
|
|
54
|
+
"""Abstract base class for issue providers.
|
|
55
|
+
|
|
56
|
+
Implementations should handle specific platforms like GitHub, GitLab, etc.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def list_issues(self, state: str = "open") -> list[Issue]:
|
|
61
|
+
"""List issues in the repository.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
state: Filter by issue state ('open', 'closed', 'all').
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List of Issue objects.
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
RuntimeError: If the operation fails.
|
|
71
|
+
"""
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def get_issue(self, identifier: str) -> Issue:
|
|
76
|
+
"""Get a specific issue by number or URL.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
identifier: Issue number (as string) or full URL.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Issue object.
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
ValueError: If the issue is not found.
|
|
86
|
+
RuntimeError: If the operation fails.
|
|
87
|
+
"""
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def is_available(self) -> bool:
|
|
92
|
+
"""Check if the provider's CLI tool is available.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
True if the CLI tool is installed and accessible.
|
|
96
|
+
"""
|
|
97
|
+
pass
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""GitHub issue provider using gh CLI."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from ensemble.issue_provider import Issue, IssueProvider
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GitHubProvider(IssueProvider):
|
|
12
|
+
"""Issue provider for GitHub using the gh CLI."""
|
|
13
|
+
|
|
14
|
+
def is_available(self) -> bool:
|
|
15
|
+
"""Check if gh CLI is installed.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
True if gh is available in PATH.
|
|
19
|
+
"""
|
|
20
|
+
return shutil.which("gh") is not None
|
|
21
|
+
|
|
22
|
+
def list_issues(self, state: str = "open") -> list[Issue]:
|
|
23
|
+
"""List issues using gh CLI.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
state: Filter by issue state ('open', 'closed', 'all').
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
List of Issue objects.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
RuntimeError: If gh command fails.
|
|
33
|
+
"""
|
|
34
|
+
cmd = [
|
|
35
|
+
"gh", "issue", "list",
|
|
36
|
+
"--state", state,
|
|
37
|
+
"--json", "number,title,body,url,state,labels",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
result = subprocess.run(
|
|
41
|
+
cmd,
|
|
42
|
+
capture_output=True,
|
|
43
|
+
text=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if result.returncode != 0:
|
|
47
|
+
raise RuntimeError(f"Failed to list issues: {result.stderr}")
|
|
48
|
+
|
|
49
|
+
data = json.loads(result.stdout)
|
|
50
|
+
return [self._parse_issue(item) for item in data]
|
|
51
|
+
|
|
52
|
+
def get_issue(self, identifier: str) -> Issue:
|
|
53
|
+
"""Get a specific issue by number or URL.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
identifier: Issue number (as string) or full URL.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Issue object.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If the issue is not found.
|
|
63
|
+
RuntimeError: If gh command fails.
|
|
64
|
+
"""
|
|
65
|
+
cmd = [
|
|
66
|
+
"gh", "issue", "view", identifier,
|
|
67
|
+
"--json", "number,title,body,url,state,labels",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
result = subprocess.run(
|
|
71
|
+
cmd,
|
|
72
|
+
capture_output=True,
|
|
73
|
+
text=True,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
if result.returncode != 0:
|
|
77
|
+
if "not found" in result.stderr.lower() or "could not find" in result.stderr.lower():
|
|
78
|
+
raise ValueError(f"Issue not found: {identifier}")
|
|
79
|
+
raise RuntimeError(f"Failed to get issue: {result.stderr}")
|
|
80
|
+
|
|
81
|
+
data = json.loads(result.stdout)
|
|
82
|
+
return self._parse_issue(data)
|
|
83
|
+
|
|
84
|
+
def _parse_issue(self, data: dict[str, Any]) -> Issue:
|
|
85
|
+
"""Parse issue data from gh JSON output.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
data: Dictionary from gh JSON output.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Issue object.
|
|
92
|
+
"""
|
|
93
|
+
# Labels come as list of dicts with 'name' key
|
|
94
|
+
labels = [label["name"] for label in data.get("labels", [])]
|
|
95
|
+
|
|
96
|
+
return Issue(
|
|
97
|
+
number=data["number"],
|
|
98
|
+
title=data["title"],
|
|
99
|
+
body=data.get("body", ""),
|
|
100
|
+
url=data["url"],
|
|
101
|
+
state=data["state"].lower(),
|
|
102
|
+
labels=labels,
|
|
103
|
+
)
|
|
@@ -45,14 +45,15 @@ model: opus
|
|
|
45
45
|
|
|
46
46
|
## パターン別実行方法
|
|
47
47
|
|
|
48
|
-
### パターンA:
|
|
48
|
+
### パターンA: 単一Worker実行
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
軽量タスクでもDispatch経由で実行する。Conductorは計画・判断・委譲のみ。
|
|
51
51
|
|
|
52
52
|
```
|
|
53
|
-
1.
|
|
54
|
-
2.
|
|
55
|
-
3.
|
|
53
|
+
1. queue/conductor/dispatch-instruction.yaml に指示を書く
|
|
54
|
+
2. worker_count: 1 で単一Workerを指定
|
|
55
|
+
3. Dispatchに通知し、Workerが実行
|
|
56
|
+
4. 完了報告を待機
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
### パターンB: shogun方式(tmux並列)
|
|
@@ -201,13 +202,25 @@ Claude Max 5並列制限を考慮:
|
|
|
201
202
|
|---------|--------|-----------|
|
|
202
203
|
| `/go` または タスク依頼 | ユーザー | 計画立案・パターン選択・実行 |
|
|
203
204
|
|
|
204
|
-
###
|
|
205
|
+
### 完了確認方法(ポーリング)
|
|
205
206
|
|
|
206
|
-
Dispatch
|
|
207
|
-
1. `status/dashboard.md` を定期的に確認
|
|
208
|
-
2. `queue/reports/` にファイルが揃ったら完了
|
|
207
|
+
Dispatchへの委譲後、以下のポーリング処理を実行:
|
|
209
208
|
|
|
210
|
-
|
|
209
|
+
```bash
|
|
210
|
+
# 完了待機ループ(30秒間隔、最大30分)
|
|
211
|
+
for i in $(seq 1 60); do
|
|
212
|
+
if [ -f "queue/reports/completion-summary.yaml" ]; then
|
|
213
|
+
echo "タスク完了を検知"
|
|
214
|
+
break
|
|
215
|
+
fi
|
|
216
|
+
sleep 30
|
|
217
|
+
done
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
完了検知後:
|
|
221
|
+
1. `queue/reports/completion-summary.yaml` を読み込む
|
|
222
|
+
2. 結果をユーザーに報告
|
|
223
|
+
3. completion-summary.yaml を削除(次回の検知のため)
|
|
211
224
|
|
|
212
225
|
## 自律判断チェックリスト
|
|
213
226
|
|
|
@@ -227,6 +240,47 @@ Dispatchはsend-keysでConductorに報告しない。以下の方法で完了を
|
|
|
227
240
|
- [ ] 未完了タスクの棚卸し
|
|
228
241
|
- [ ] queue/ 内の古いファイル削除
|
|
229
242
|
|
|
243
|
+
## セッション構成
|
|
244
|
+
|
|
245
|
+
Ensembleは2つの独立したtmuxセッションで動作する:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
セッション1: ensemble-conductor
|
|
249
|
+
+------------------+------------------+
|
|
250
|
+
| Conductor | dashboard |
|
|
251
|
+
+------------------+------------------+
|
|
252
|
+
|
|
253
|
+
セッション2: ensemble-workers
|
|
254
|
+
+------------------+----------+
|
|
255
|
+
| | worker-1 |
|
|
256
|
+
| dispatch +----------+
|
|
257
|
+
| | worker-2 |
|
|
258
|
+
+------------------+----------+
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 2つのターミナルで同時表示
|
|
262
|
+
|
|
263
|
+
別々のターミナルウィンドウで各セッションをアタッチすることで、
|
|
264
|
+
Conductor(+dashboard)とWorkers(dispatch+workers)両方を同時に監視できる:
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
# ターミナル1
|
|
268
|
+
tmux attach -t ensemble-conductor
|
|
269
|
+
|
|
270
|
+
# ターミナル2
|
|
271
|
+
tmux attach -t ensemble-workers
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### セッション間通信
|
|
275
|
+
|
|
276
|
+
セッションが分かれていてもsend-keysで通信可能:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
source .ensemble/panes.env
|
|
280
|
+
tmux send-keys -t "$CONDUCTOR_PANE" 'message' Enter
|
|
281
|
+
tmux send-keys -t "$DISPATCH_PANE" 'message' Enter
|
|
282
|
+
```
|
|
283
|
+
|
|
230
284
|
## 禁止事項
|
|
231
285
|
|
|
232
286
|
- 自分でコードを書く
|
|
@@ -78,13 +78,35 @@ ls queue/reports/*.yaml
|
|
|
78
78
|
```
|
|
79
79
|
通信ロストした他ワーカーの報告も拾える。
|
|
80
80
|
|
|
81
|
-
## Conductor
|
|
81
|
+
## Conductor への報告方法(ファイルベース + 通知)
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
### 1. ファイル報告(プライマリ)
|
|
84
|
+
**Conductorにsend-keysを送らない**。結果はファイルで報告:
|
|
85
|
+
1. `status/dashboard.md` を更新(完了ステータスに)
|
|
86
|
+
2. `queue/reports/completion-summary.yaml` に集約結果を記載
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
### 2. 通知(セカンダリ)
|
|
89
|
+
completion-summary.yaml作成後、Conductorに完了を通知:
|
|
90
|
+
|
|
91
|
+
#### 通知手順
|
|
92
|
+
```bash
|
|
93
|
+
# panes.envを読み込む
|
|
94
|
+
source .ensemble/panes.env
|
|
95
|
+
|
|
96
|
+
# Conductorに通知(2回分割)
|
|
97
|
+
tmux send-keys -t "$CONDUCTOR_PANE" '全タスク完了。queue/reports/completion-summary.yamlを確認してください'
|
|
98
|
+
sleep 1
|
|
99
|
+
tmux send-keys -t "$CONDUCTOR_PANE" Enter
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
#### 通知失敗時
|
|
103
|
+
- send-keysが失敗してもエラーにしない
|
|
104
|
+
- Conductorがポーリングでフォールバック
|
|
105
|
+
- ファイル報告が最優先(通知は補助)
|
|
106
|
+
|
|
107
|
+
### 3. ポーリング(フォールバック)
|
|
108
|
+
Conductorは `queue/reports/completion-summary.yaml` の存在を検知して完了を把握する。
|
|
109
|
+
通知は効率化のための補助機能。
|
|
88
110
|
|
|
89
111
|
---
|
|
90
112
|
|
|
@@ -155,22 +177,46 @@ workflow: default
|
|
|
155
177
|
pattern: B
|
|
156
178
|
```
|
|
157
179
|
|
|
158
|
-
##
|
|
180
|
+
## ウィンドウ・ペイン構成
|
|
181
|
+
|
|
182
|
+
Ensembleは2ウィンドウ構成で動作する:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
ウィンドウ1: conductor(Conductorがいる場所)
|
|
186
|
+
+----------------------------------+
|
|
187
|
+
| Conductor |
|
|
188
|
+
+----------------------------------+
|
|
189
|
+
|
|
190
|
+
ウィンドウ2: workers(あなたがいる場所)
|
|
191
|
+
+------------------+----------+
|
|
192
|
+
| dispatch | worker-1 |
|
|
193
|
+
| (あなた) +----------+
|
|
194
|
+
+------------------+ worker-2 |
|
|
195
|
+
| dashboard +----------+
|
|
196
|
+
| | ... |
|
|
197
|
+
+------------------+----------+
|
|
198
|
+
```
|
|
159
199
|
|
|
160
200
|
ペイン番号ではなくペインIDで管理する。`.ensemble/panes.env` を参照。
|
|
161
201
|
|
|
162
202
|
```
|
|
203
|
+
ウィンドウ名:
|
|
204
|
+
$CONDUCTOR_WINDOW: conductor
|
|
205
|
+
$WORKERS_WINDOW: workers
|
|
206
|
+
|
|
163
207
|
初期状態:
|
|
164
|
-
$CONDUCTOR_PANE: Conductor
|
|
208
|
+
$CONDUCTOR_PANE: Conductor(別ウィンドウ)
|
|
165
209
|
$DISPATCH_PANE: Dispatch(自分)
|
|
166
210
|
$DASHBOARD_PANE: Dashboard
|
|
211
|
+
$WORKER_AREA_PANE: ワーカー用プレースホルダー
|
|
167
212
|
|
|
168
213
|
ワーカー追加後(例: 2ワーカー):
|
|
169
|
-
$CONDUCTOR_PANE: Conductor
|
|
214
|
+
$CONDUCTOR_PANE: Conductor(別ウィンドウ)
|
|
170
215
|
$DISPATCH_PANE: Dispatch(自分)
|
|
216
|
+
$DASHBOARD_PANE: Dashboard
|
|
171
217
|
$WORKER_1_PANE: Worker-1 (WORKER_ID=1)
|
|
172
218
|
$WORKER_2_PANE: Worker-2 (WORKER_ID=2)
|
|
173
|
-
$
|
|
219
|
+
$WORKER_COUNT: 2
|
|
174
220
|
```
|
|
175
221
|
|
|
176
222
|
## タスク配信の具体手順
|
|
@@ -218,13 +264,39 @@ done
|
|
|
218
264
|
|
|
219
265
|
## 完了報告の収集
|
|
220
266
|
|
|
267
|
+
### プライマリ: 通知ベース
|
|
268
|
+
Workerから「タスク${TASK_ID}完了」の通知を受けたら、
|
|
269
|
+
queue/reports/${TASK_ID}-completed.yaml を確認する。
|
|
270
|
+
|
|
271
|
+
### フォールバック: ポーリング
|
|
272
|
+
タイムアウト(例: 3分)後、Workerから通知がない場合:
|
|
273
|
+
1. queue/reports/ を全スキャン
|
|
274
|
+
2. 新しい完了報告ファイルを検出
|
|
275
|
+
3. 未検出のタスクは「進行中」と判断し、追加で2分待機
|
|
276
|
+
4. 再度タイムアウト → エスカレーション
|
|
277
|
+
|
|
278
|
+
### 実装例
|
|
221
279
|
```bash
|
|
222
|
-
#
|
|
223
|
-
|
|
280
|
+
# タイムアウト後のスキャン
|
|
281
|
+
echo "⏰ タイムアウト。完了報告をスキャン中..."
|
|
282
|
+
ls queue/reports/*.yaml | grep -v escalation | grep -v completion-summary
|
|
283
|
+
|
|
284
|
+
# 各タスクの完了状態を確認
|
|
285
|
+
for task_id in task-001 task-002 ...; do
|
|
286
|
+
if [ -f "queue/reports/${task_id}-completed.yaml" ]; then
|
|
287
|
+
echo "✅ ${task_id}: 完了済み(通知なしで検出)"
|
|
288
|
+
else
|
|
289
|
+
echo "⏳ ${task_id}: 未完了"
|
|
290
|
+
fi
|
|
291
|
+
done
|
|
292
|
+
```
|
|
224
293
|
|
|
225
|
-
|
|
226
|
-
|
|
294
|
+
### 報告の全確認原則(既存ルールを強調)
|
|
295
|
+
Workerから起こされたら、起こした1人だけでなく**全ワーカーの報告ファイルをスキャン**:
|
|
296
|
+
```bash
|
|
297
|
+
ls queue/reports/*.yaml
|
|
227
298
|
```
|
|
299
|
+
通信ロストした他ワーカーの報告も拾える。
|
|
228
300
|
|
|
229
301
|
## 完了判定と報告フロー
|
|
230
302
|
|
|
@@ -232,8 +304,11 @@ cat queue/reports/${TASK_ID}.yaml
|
|
|
232
304
|
1. Workerから「タスク${TASK_ID}完了」の通知を受ける
|
|
233
305
|
2. queue/reports/${TASK_ID}.yaml を確認
|
|
234
306
|
3. 全タスクの完了を待つ
|
|
235
|
-
4.
|
|
307
|
+
4. 結果を集約:
|
|
308
|
+
- status/dashboard.md を「完了」に更新
|
|
309
|
+
- queue/reports/completion-summary.yaml を作成(Conductorがポーリングで検知)
|
|
236
310
|
5. queue/conductor/dispatch-instruction.yaml を削除(処理済み)
|
|
311
|
+
6. 「待機中」と表示して次の指示を待つ
|
|
237
312
|
```
|
|
238
313
|
|
|
239
314
|
## 結果集約フォーマット
|
|
@@ -340,6 +415,45 @@ worker_id と executed_by が不一致の場合:
|
|
|
340
415
|
- [ ] ワーカーが異常終了した
|
|
341
416
|
- [ ] 指示ファイルのフォーマットが不正
|
|
342
417
|
|
|
418
|
+
## /clear プロトコル(Worker コンテキスト管理)
|
|
419
|
+
|
|
420
|
+
Workerのコンテキスト蓄積を防ぐため、タスク完了後に `/clear` を送信する。
|
|
421
|
+
|
|
422
|
+
### いつ /clear を送るか
|
|
423
|
+
- **タスク完了報告受信後、次タスク割当前** に送る
|
|
424
|
+
- Worker完了報告 → dashboard更新 → **/clear送信** → 次タスク指示
|
|
425
|
+
|
|
426
|
+
### /clear 送信手順
|
|
427
|
+
```bash
|
|
428
|
+
# 1. 次タスクYAMLを先に書き込む(Worker復帰後にすぐ読めるように)
|
|
429
|
+
# queue/tasks/worker-{N}-task.yaml に次タスクを書く
|
|
430
|
+
|
|
431
|
+
# 2. /clear を send-keys で送る
|
|
432
|
+
source .ensemble/panes.env
|
|
433
|
+
tmux send-keys -t "$WORKER_{N}_PANE" '/clear'
|
|
434
|
+
sleep 1
|
|
435
|
+
tmux send-keys -t "$WORKER_{N}_PANE" Enter
|
|
436
|
+
|
|
437
|
+
# 3. Worker復帰を待つ(約5秒)
|
|
438
|
+
sleep 5
|
|
439
|
+
|
|
440
|
+
# 4. タスク読み込み指示を送る
|
|
441
|
+
tmux send-keys -t "$WORKER_{N}_PANE" 'queue/tasks/にタスクがあります。確認して実行してください。'
|
|
442
|
+
sleep 1
|
|
443
|
+
tmux send-keys -t "$WORKER_{N}_PANE" Enter
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### /clear をスキップする場合
|
|
447
|
+
以下の条件ではスキップ可:
|
|
448
|
+
- 短タスク連続(推定5分以内)
|
|
449
|
+
- 同一ファイル群の連続タスク
|
|
450
|
+
- Workerのコンテキストがまだ軽量(タスク2件目以内)
|
|
451
|
+
|
|
452
|
+
### Conductor / Dispatch は /clear しない
|
|
453
|
+
- **Dispatch**: 全Worker状態を把握する必要がある
|
|
454
|
+
- **Conductor**: プロジェクト全体像・計画を維持する必要がある
|
|
455
|
+
- コンテキスト逼迫時は `/compact` を自己判断で実行
|
|
456
|
+
|
|
343
457
|
## 禁止事項
|
|
344
458
|
|
|
345
459
|
- タスクの内容を判断する
|
|
@@ -115,6 +115,31 @@ errors: [] # エラーがあれば記載
|
|
|
115
115
|
completed_at: "2026-02-03T10:30:00Z"
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
+
## 完了報告後の通知プロトコル
|
|
119
|
+
|
|
120
|
+
完了報告ファイル(queue/reports/task-XXX-completed.yaml)を作成した後、
|
|
121
|
+
必ずDispatchペインに通知せよ:
|
|
122
|
+
|
|
123
|
+
### 通知手順
|
|
124
|
+
1. panes.envを読み込む
|
|
125
|
+
2. DISPATCH_PANEにsend-keysで通知(2回分割)
|
|
126
|
+
3. フォーマット: 「タスク${TASK_ID}完了。queue/reports/をご確認ください」
|
|
127
|
+
|
|
128
|
+
### 実装例
|
|
129
|
+
```bash
|
|
130
|
+
# panes.envを読み込む
|
|
131
|
+
source .ensemble/panes.env
|
|
132
|
+
|
|
133
|
+
# Dispatchに通知(2回分割)
|
|
134
|
+
tmux send-keys -t "$DISPATCH_PANE" 'タスク${TASK_ID}完了。queue/reports/をご確認ください'
|
|
135
|
+
sleep 1
|
|
136
|
+
tmux send-keys -t "$DISPATCH_PANE" Enter
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 通知失敗時
|
|
140
|
+
- send-keysが失敗してもエラーにしない(Dispatchがポーリングでフォールバック)
|
|
141
|
+
- 完了報告ファイルが最優先(通知は補助)
|
|
142
|
+
|
|
118
143
|
## エラー発生時
|
|
119
144
|
|
|
120
145
|
1. エラー内容を完了報告に記載
|
|
@@ -172,6 +197,36 @@ completed_at: "2026-02-03T10:30:00Z"
|
|
|
172
197
|
4. 報告時に `executed_by: worker-{自分のID}` を必ず記載
|
|
173
198
|
5. 元のワーカーの報告ファイルに書き込む(ファイル名は変えない)
|
|
174
199
|
|
|
200
|
+
## /clear 後の復帰手順
|
|
201
|
+
|
|
202
|
+
Dispatchから `/clear` を受けた後、以下の手順で最小コストで復帰する。
|
|
203
|
+
|
|
204
|
+
### 復帰フロー(約3,000トークンで復帰)
|
|
205
|
+
```
|
|
206
|
+
/clear 実行
|
|
207
|
+
│
|
|
208
|
+
▼ CLAUDE.md 自動読み込み
|
|
209
|
+
│
|
|
210
|
+
▼ Step 1: 自分のWorker IDを確認
|
|
211
|
+
│ echo $WORKER_ID
|
|
212
|
+
│ → 出力例: 1 → 自分はWorker-1
|
|
213
|
+
│
|
|
214
|
+
▼ Step 2: 自分のタスクYAML読み込み
|
|
215
|
+
│ queue/tasks/worker-{N}-task.yaml を読む
|
|
216
|
+
│ → タスクがあれば作業開始
|
|
217
|
+
│ → なければ次の指示を待つ
|
|
218
|
+
│
|
|
219
|
+
▼ Step 3: 必要に応じて追加コンテキスト読み込み
|
|
220
|
+
│ タスクYAMLに files フィールドがあれば対象ファイルを読む
|
|
221
|
+
│
|
|
222
|
+
▼ 作業開始
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### /clear 復帰の注意事項
|
|
226
|
+
- /clear前のタスクの記憶は消えている。タスクYAMLだけを信頼せよ
|
|
227
|
+
- instructions(エージェント定義)は初回は読まなくてよい(CLAUDE.mdで十分)
|
|
228
|
+
- 2タスク目以降で詳細な手順が必要なら worker.md を読む
|
|
229
|
+
|
|
175
230
|
## 禁止事項
|
|
176
231
|
|
|
177
232
|
- 担当外のファイルを編集する
|