Skip to content

Commit 8e44aa1

Browse files
darkspockclaude
andcommitted
Update SDKs, roadmap, and epic task tracking
- Go SDK: add missing CancelOrchestra method - Laravel SDK: add all orchestra, secrets, artifacts, and result methods - Roadmap: add EPIC-09 (~90%) and EPIC-10 (~30%) - EPIC-09 tasks: mark completed phases (1-8 mostly done, 9 pending) - EPIC-10 tasks: create detailed task breakdown document Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 381353c commit 8e44aa1

5 files changed

Lines changed: 402 additions & 128 deletions

File tree

docs/epics/epic-09-tasks.md

Lines changed: 126 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,118 @@
11
# EPIC-09 Tasks: Orchestras — Dynamic Workflow Orchestration
22

3-
> Status: NOT STARTED — created 2026-03-19
3+
> Status: ~90% — backend implemented, frontend dashboard partial — updated 2026-03-19
44
55
## Phase 1: Foundation (Result + Secrets + Artifacts)
66

77
### T09.1 Run Result (JSONB)
8-
- [ ] Add `result JSONB` column to runs table (migration)
9-
- [ ] `PATCH /runs/{id}/result` — set result (max 1MB, validates JSON)
10-
- [ ] `GET /runs/{id}/result` — get result
11-
- [ ] SDK: `set_result(run_id, data)` and `get_result(run_id)` in all 5 SDKs
12-
- [ ] `CRONCONTROL_TRIGGERED_BY` env var injected when run is triggered by another run
8+
- [x] Add `result JSONB` column to runs table (migration)
9+
- [x] `PATCH /runs/{id}/result` — set result (max 1MB, validates JSON)
10+
- [x] `GET /runs/{id}/result` — get result
11+
- [x] SDK: `set_result(run_id, data)` and `get_result(run_id)` in all 5 SDKs
12+
- [x] `CRONCONTROL_TRIGGERED_BY` env var injected when run is triggered by another run
1313

1414
### T09.2 Workspace Secrets Vault
15-
- [ ] `workspace_secrets` table: id, workspace_id, name, value_enc (AES-256-GCM), created_at
16-
- [ ] `GET /secrets` — list names only, never values
17-
- [ ] `POST /secrets` — create (name + value, encrypted at rest)
18-
- [ ] `PUT /secrets/{name}` — update value
19-
- [ ] `DELETE /secrets/{name}` — delete
15+
- [x] `workspace_secrets` table: id, workspace_id, name, value_enc (AES-256-GCM), created_at
16+
- [x] `GET /secrets` — list names only, never values
17+
- [x] `POST /secrets` — create (name + value, encrypted at rest)
18+
- [x] `PUT /secrets/{name}` — update value
19+
- [x] `DELETE /secrets/{name}` — delete
2020
- [ ] Dashboard: Settings > Secrets tab (list, create, delete, warning about one-time visibility)
21-
- [ ] SDK: `create_secret`, `list_secrets`, `delete_secret` in all 5 SDKs
21+
- [x] SDK: `create_secret`, `list_secrets`, `delete_secret` in all 5 SDKs
2222

2323
### T09.3 Run Artifacts (S3/MinIO + Local)
24-
- [ ] `run_artifacts` table: id, run_id, workspace_id, name, content_type, size_bytes, storage_key
25-
- [ ] Artifact storage backend interface: `Upload`, `Download`, `Delete`, `GetURL`
26-
- [ ] S3/MinIO backend implementation
27-
- [ ] Local filesystem backend (fallback)
28-
- [ ] `POST /runs/{id}/artifacts` — upload (multipart)
29-
- [ ] `GET /runs/{id}/artifacts` — list
30-
- [ ] `GET /runs/{id}/artifacts/{name}` — download
31-
- [ ] Config: `CC_ARTIFACTS_BACKEND`, `CC_ARTIFACTS_S3_ENDPOINT`, `CC_ARTIFACTS_S3_BUCKET`, etc.
32-
- [ ] SDK: `upload_artifact`, `get_artifact_url`, `list_artifacts` in all 5 SDKs
24+
- [x] `run_artifacts` table: id, run_id, workspace_id, name, content_type, size_bytes, storage_key
25+
- [x] Artifact storage backend interface: `Upload`, `Download`, `Delete`, `GetURL`
26+
- [x] S3/MinIO backend implementation
27+
- [x] Local filesystem backend (fallback)
28+
- [x] `POST /runs/{id}/artifacts` — upload (multipart)
29+
- [x] `GET /runs/{id}/artifacts` — list
30+
- [x] `GET /runs/{id}/artifacts/{name}` — download
31+
- [x] Config: `CC_ARTIFACTS_BACKEND`, `CC_ARTIFACTS_S3_ENDPOINT`, `CC_ARTIFACTS_S3_BUCKET`, etc.
32+
- [x] SDK: `upload_artifact`, `get_artifact_url`, `list_artifacts` in all 5 SDKs
3333

3434
## Phase 2: Orchestra Core
3535

3636
### T09.4 Orchestra Table & Lifecycle
37-
- [ ] `orchestras` table: id, workspace_id, name, director_type, director_process_id, ai_config, state, movement_count, secrets, summary, parent_orchestra_id, wait_for_parent, budget, budget_used, timeout, timeout_at, created_at, updated_at
38-
- [ ] Orchestra states: active, waiting_for_choice, paused, completed, cancelled, failed
39-
- [ ] `POST /orchestras` — create (name, director, first_musician, secrets, timeout, budget)
40-
- [ ] `GET /orchestras` — list (filter by state)
41-
- [ ] `GET /orchestras/{id}` — get metadata
42-
- [ ] `GET /orchestras/{id}/score` — full score (all movements with results)
43-
- [ ] `POST /orchestras/{id}/cancel` — cancel active orchestra
44-
- [ ] Add `orchestra_id`, `orchestra_step` columns to runs table (migration)
45-
- [ ] SDK: `create_orchestra`, `get_score`, `cancel_orchestra` in all 5 SDKs
37+
- [x] `orchestras` table: id, workspace_id, name, director_type, director_process_id, ai_config, state, movement_count, secrets, summary, parent_orchestra_id, wait_for_parent, budget, budget_used, timeout, timeout_at, created_at, updated_at
38+
- [x] Orchestra states: active, waiting_for_choice, paused, completed, cancelled, failed
39+
- [x] `POST /orchestras` — create (name, director, first_musician, secrets, timeout, budget)
40+
- [x] `GET /orchestras` — list (filter by state)
41+
- [x] `GET /orchestras/{id}` — get metadata
42+
- [x] `GET /orchestras/{id}/score` — full score (all movements with results)
43+
- [x] `POST /orchestras/{id}/cancel` — cancel active orchestra
44+
- [x] Add `orchestra_id`, `orchestra_step` columns to runs table (migration)
45+
- [x] SDK: `create_orchestra`, `get_score`, `cancel_orchestra` in all 5 SDKs
4646

4747
### T09.5 Next Movement (Dynamic Trigger)
48-
- [ ] `POST /runs/{id}/next` — trigger next movement (process_id, payload, message)
49-
- [ ] Creates new run with: orchestra_id, orchestra_step+1, triggered_by_run_id
50-
- [ ] Inherits orchestra secrets as env vars
51-
- [ ] Injects: `CRONCONTROL_ORCHESTRA_ID`, `CRONCONTROL_ORCHESTRA_STEP`, `CRONCONTROL_TRIGGERED_BY`
52-
- [ ] Increments orchestra movement_count
53-
- [ ] SDK: `next_movement(process_id, payload, message)` in all 5 SDKs
48+
- [x] `POST /runs/{id}/next` — trigger next movement (process_id, payload, message)
49+
- [x] Creates new run with: orchestra_id, orchestra_step+1, triggered_by_run_id
50+
- [x] Inherits orchestra secrets as env vars
51+
- [x] Injects: `CRONCONTROL_ORCHESTRA_ID`, `CRONCONTROL_ORCHESTRA_STEP`, `CRONCONTROL_TRIGGERED_BY`
52+
- [x] Increments orchestra movement_count
53+
- [x] SDK: `next_movement(process_id, payload, message)` in all 5 SDKs
5454

5555
### T09.6 Director Auto-Trigger
56-
- [ ] After movement completes/fails: if orchestra has a director, trigger it automatically
57-
- [ ] Inject event env vars: `CRONCONTROL_EVENT_TYPE`, `CRONCONTROL_EVENT_RUN_ID`, `CRONCONTROL_EVENT_RESULT`
58-
- [ ] Director process runs, reads event via `cc.get_event()`, decides next action
59-
- [ ] SDK: `get_event()` (reads env vars, returns structured event) in all 5 SDKs
60-
- [ ] If director_type=none: no auto-trigger (musicians chain directly)
56+
- [x] After movement completes/fails: if orchestra has a director, trigger it automatically
57+
- [x] Inject event env vars: `CRONCONTROL_EVENT_TYPE`, `CRONCONTROL_EVENT_RUN_ID`, `CRONCONTROL_EVENT_RESULT`
58+
- [x] Director process runs, reads event via `cc.get_event()`, decides next action
59+
- [x] SDK: `get_event()` (reads env vars, returns structured event) in all 5 SDKs
60+
- [x] If director_type=none: no auto-trigger (musicians chain directly)
6161

6262
### T09.7 Ask Choice (Human-in-the-Loop)
63-
- [ ] Add `choice_config JSONB`, `chosen_index INTEGER` columns to runs table
64-
- [ ] Add `waiting_for_choice` run state + state machine transitions
65-
- [ ] `POST /runs/{id}/choice` — set choice config (message + choices array)
66-
- [ ] `POST /runs/{id}/choose` — human selects a choice (choice_index)
67-
- [ ] On choose: if choice has process_id → trigger next movement; if null → finish/cancel
68-
- [ ] Update orchestra state: active ↔ waiting_for_choice
69-
- [ ] SDK: `ask_choice(run_id, message, choices)` and `ask_confirm(run_id, message, on_approve, on_reject)` in all 5 SDKs
70-
- [ ] Dashboard RunDetail: show choice buttons when waiting_for_choice
63+
- [x] Add `choice_config JSONB`, `chosen_index INTEGER` columns to runs table
64+
- [x] Add `waiting_for_choice` run state + state machine transitions
65+
- [x] `POST /runs/{id}/choice` — set choice config (message + choices array)
66+
- [x] `POST /runs/{id}/choose` — human selects a choice (choice_index)
67+
- [x] On choose: if choice has process_id → trigger next movement; if null → finish/cancel
68+
- [x] Update orchestra state: active ↔ waiting_for_choice
69+
- [x] SDK: `ask_choice(run_id, message, choices)` and `ask_confirm(run_id, message, on_approve, on_reject)` in all 5 SDKs
70+
- [x] Dashboard RunDetail: show choice buttons when waiting_for_choice
7171

7272
### T09.8 Finish Orchestra
73-
- [ ] `POST /orchestras/{id}/finish` — set state=completed, summary
74-
- [ ] SDK: `finish_orchestra(orchestra_id, summary)` in all 5 SDKs
75-
- [ ] Webhook event: `orchestra.completed`
73+
- [x] `POST /orchestras/{id}/finish` — set state=completed, summary
74+
- [x] SDK: `finish_orchestra(orchestra_id, summary)` in all 5 SDKs
75+
- [x] Webhook event: `orchestra.completed`
7676

7777
## Phase 3: Chat System
7878

7979
### T09.9 Orchestra Chat Table & API
80-
- [ ] `orchestra_chat` table: id, orchestra_id, sender_type (system/director/musician/human), sender_id, message_type (text/result/request_help/action/choice/choice_response/file/status/warning), content, data JSONB, created_at
81-
- [ ] `POST /orchestras/{id}/chat` — post message
82-
- [ ] `GET /orchestras/{id}/chat` — list messages (pagination, since=timestamp)
83-
- [ ] System messages auto-posted: movement started/completed/failed, budget warnings
84-
- [ ] SDK: `post_chat(orchestra_id, message, type, data)` and `get_chat(orchestra_id, since)` in all 5 SDKs
80+
- [x] `orchestra_chat` table: id, orchestra_id, sender_type (system/director/musician/human), sender_id, message_type (text/result/request_help/action/choice/choice_response/file/status/warning), content, data JSONB, created_at
81+
- [x] `POST /orchestras/{id}/chat` — post message
82+
- [x] `GET /orchestras/{id}/chat` — list messages (pagination, since=timestamp)
83+
- [x] System messages auto-posted: movement started/completed/failed, budget warnings
84+
- [x] SDK: `post_chat(orchestra_id, message, type, data)` and `get_chat(orchestra_id, since)` in all 5 SDKs
8585

8686
### T09.10 Chat Actions
87-
- [ ] Actions field in chat messages: array of {label, action_name, params, style}
88-
- [ ] `POST /orchestras/{id}/chat/{message_id}/action` — trigger an action
89-
- [ ] When action triggered: creates a new run for the action handler OR triggers director with event
90-
- [ ] SDK: `register_action(name, handler)` and `post_chat(..., actions=[...])` in all 5 SDKs
87+
- [x] Actions field in chat messages: array of {label, action_name, params, style}
88+
- [x] `POST /orchestras/{id}/chat/{message_id}/action` — trigger an action
89+
- [x] When action triggered: creates a new run for the action handler OR triggers director with event
90+
- [x] SDK: `register_action(name, handler)` and `post_chat(..., actions=[...])` in all 5 SDKs
9191

9292
### T09.11 Real-Time Chat (SSE)
93-
- [ ] `GET /orchestras/{id}/stream` — SSE endpoint
94-
- [ ] Events: chat, movement, choice, state, budget_warning
95-
- [ ] Dashboard: real-time chat panel on Score page
96-
- [ ] Human input box: post text messages and click action buttons
93+
- [x] `GET /orchestras/{id}/stream` — SSE endpoint
94+
- [x] Events: chat, movement, choice, state, budget_warning
95+
- [x] Dashboard: real-time chat panel on Score page
96+
- [x] Human input box: post text messages and click action buttons
9797

9898
## Phase 4: AI Director
9999

100100
### T09.12 Multi-Model AI Director
101-
- [ ] Built-in Go component: `internal/orchestra/ai_director.go`
102-
- [ ] Provider interface: `CallLLM(model, system, messages, tools) → response`
103-
- [ ] Anthropic provider (Claude API with tool_use)
104-
- [ ] OpenAI provider (GPT API with function_calling)
105-
- [ ] Google provider (Gemini API with function_calling)
106-
- [ ] Tools: next_movement, ask_choice, post_chat, launch_sub_orchestra, finish_orchestra, get_score, list_musicians
107-
- [ ] System prompt includes: orchestra name, available musicians, budget remaining
108-
- [ ] Context includes: full score, last event, chat history
109-
- [ ] Fallback: 3 retries, then ask_choice to human with available options
101+
- [x] Built-in Go component: `internal/orchestra/ai_director.go`
102+
- [x] Provider interface: `CallLLM(model, system, messages, tools) → response`
103+
- [x] Anthropic provider (Claude API with tool_use)
104+
- [x] OpenAI provider (GPT API with function_calling)
105+
- [x] Google provider (Gemini API with function_calling)
106+
- [x] Tools: next_movement, ask_choice, post_chat, launch_sub_orchestra, finish_orchestra, get_score, list_musicians
107+
- [x] System prompt includes: orchestra name, available musicians, budget remaining
108+
- [x] Context includes: full score, last event, chat history
109+
- [x] Fallback: 3 retries, then ask_choice to human with available options
110110

111111
### T09.13 AI Director Budget Tracking
112-
- [ ] Count AI calls per orchestra in budget_used
113-
- [ ] Warning at 80% in chat
114-
- [ ] Fail orchestra at 100%
115-
- [ ] Webhook events: `orchestra.budget_warning`, `orchestra.budget_exceeded`
112+
- [x] Count AI calls per orchestra in budget_used
113+
- [x] Warning at 80% in chat
114+
- [x] Fail orchestra at 100%
115+
- [x] Webhook events: `orchestra.budget_warning`, `orchestra.budget_exceeded`
116116

117117
## ~~Phase 5: Sub-Orchestras~~ DEFERRED
118118
> Moved to future epic. Orchestras are flat for now — no nesting.
@@ -121,72 +121,72 @@
121121

122122

123123
### T09.16 Container Executor
124-
- [ ] `internal/executor/container/container.go`: implements Method interface
125-
- [ ] Docker SDK client (`github.com/docker/docker/client`)
126-
- [ ] `Execute()`: create Swarm service → poll tasks → collect logs → remove service
127-
- [ ] `Kill()`: remove service
128-
- [ ] Resource limits: cpu, memory from method_config
129-
- [ ] Env var injection: all standard vars + orchestra secrets
130-
- [ ] Config: `CC_CONTAINER_SWARM_ENDPOINT`, `CC_CONTAINER_DEFAULT_CPU`, `CC_CONTAINER_DEFAULT_MEMORY`
124+
- [x] `internal/executor/container/container.go`: implements Method interface
125+
- [x] Docker SDK client (`github.com/docker/docker/client`)
126+
- [x] `Execute()`: create Swarm service → poll tasks → collect logs → remove service
127+
- [x] `Kill()`: remove service
128+
- [x] Resource limits: cpu, memory from method_config
129+
- [x] Env var injection: all standard vars + orchestra secrets
130+
- [x] Config: `CC_CONTAINER_SWARM_ENDPOINT`, `CC_CONTAINER_DEFAULT_CPU`, `CC_CONTAINER_DEFAULT_MEMORY`
131131

132132
### T09.17 Registry Authentication
133-
- [ ] method_config.registry_auth references a workspace secret
134-
- [ ] Container executor decrypts and passes to `docker service create --with-registry-auth`
135-
- [ ] Support: Docker Hub, ghcr.io, AWS ECR, custom registries
133+
- [x] method_config.registry_auth references a workspace secret
134+
- [x] Container executor decrypts and passes to `docker service create --with-registry-auth`
135+
- [x] Support: Docker Hub, ghcr.io, AWS ECR, custom registries
136136

137137
### T09.18 Container Pool Dashboard
138-
- [ ] `GET /admin/container-pool` — list Swarm nodes (platform admin)
138+
- [x] `GET /admin/container-pool` — list Swarm nodes (platform admin)
139139
- [ ] Dashboard: Settings > Container Pool (node list, CPU/memory usage, containers running)
140140
- [ ] Health status per node
141141

142142
## Phase 7: Timeout, Budget & Pause
143143

144144
### T09.19 Orchestra Timeout
145-
- [ ] `timeout` and `timeout_at` fields on orchestras table
146-
- [ ] Monitor goroutine checks active orchestras for timeout
147-
- [ ] On timeout: kill running movements, state=failed, chat message, webhook `orchestra.timeout`
145+
- [x] `timeout` and `timeout_at` fields on orchestras table
146+
- [x] Monitor goroutine checks active orchestras for timeout
147+
- [x] On timeout: kill running movements, state=failed, chat message, webhook `orchestra.timeout`
148148

149149
### T09.20 Orchestra Budget
150-
- [ ] `budget` and `budget_used` JSONB fields on orchestras table
151-
- [ ] Counters: movements, ai_calls, duration_minutes
152-
- [ ] Check before each movement/AI call
153-
- [ ] 80% warning: chat + webhook
154-
- [ ] 100% exceeded: fail orchestra + webhook
150+
- [x] `budget` and `budget_used` JSONB fields on orchestras table
151+
- [x] Counters: movements, ai_calls, duration_minutes
152+
- [x] Check before each movement/AI call
153+
- [x] 80% warning: chat + webhook
154+
- [x] 100% exceeded: fail orchestra + webhook
155155

156156
### T09.21 Pause / Resume
157-
- [ ] `POST /orchestras/{id}/pause` — state=paused, no new movements triggered
158-
- [ ] `POST /orchestras/{id}/resume` — state=active, process queued events
159-
- [ ] Running movements continue to completion during pause
160-
- [ ] Completed events queued (stored in orchestra_events table or chat)
161-
- [ ] On resume: director triggered with accumulated events
162-
- [ ] SDK: `pause_orchestra(id)` and `resume_orchestra(id)` in all 5 SDKs
157+
- [x] `POST /orchestras/{id}/pause` — state=paused, no new movements triggered
158+
- [x] `POST /orchestras/{id}/resume` — state=active, process queued events
159+
- [x] Running movements continue to completion during pause
160+
- [x] Completed events queued (stored in orchestra_events table or chat)
161+
- [x] On resume: director triggered with accumulated events
162+
- [x] SDK: `pause_orchestra(id)` and `resume_orchestra(id)` in all 5 SDKs
163163

164164
## Phase 8: Dashboard
165165

166166
### T09.22 Orchestra List Page
167-
- [ ] New sidebar item: "Orchestras" (between Queue and Infrastructure sections)
168-
- [ ] List: name, director type badge, state badge, movement count, duration, budget used
169-
- [ ] Filters: state, date range
170-
- [ ] Click → Score view
167+
- [x] New sidebar item: "Orchestras" (between Queue and Infrastructure sections)
168+
- [x] List: name, director type badge, state badge, movement count, duration, budget used
169+
- [x] Filters: state, date range
170+
- [x] Click → Score view
171171

172172
### T09.23 Score View Page
173-
- [ ] `/orchestras/{id}` route
174-
- [ ] Vertical timeline of movements: process name, state, duration, result preview
175-
- [ ] Choice buttons inline when waiting_for_choice
176-
- [ ] Orchestra metadata header: name, director, state, budget, timeout
173+
- [x] `/orchestras/{id}` route
174+
- [x] Vertical timeline of movements: process name, state, duration, result preview
175+
- [x] Choice buttons inline when waiting_for_choice
176+
- [x] Orchestra metadata header: name, director, state, budget, timeout
177177
- [ ] Sub-orchestras expandable tree
178178

179179
### T09.24 Chat Panel
180-
- [ ] Real-time chat panel (SSE) on Score page
181-
- [ ] Message types rendered differently: text, status (gray), warning (amber), file (link), action (buttons)
182-
- [ ] Human input box at bottom
183-
- [ ] Action buttons clickable
180+
- [x] Real-time chat panel (SSE) on Score page
181+
- [x] Message types rendered differently: text, status (gray), warning (amber), file (link), action (buttons)
182+
- [x] Human input box at bottom
183+
- [x] Action buttons clickable
184184

185185
### T09.25 RunDetail Orchestra Integration
186-
- [ ] Show result JSON (collapsible section)
187-
- [ ] Show artifacts with download links
188-
- [ ] Orchestra breadcrumb: Orchestra "name" → Movement N
189-
- [ ] Choice buttons when waiting_for_choice
186+
- [x] Show result JSON (collapsible section)
187+
- [x] Show artifacts with download links
188+
- [x] Orchestra breadcrumb: Orchestra "name" → Movement N
189+
- [x] Choice buttons when waiting_for_choice
190190

191191
## Phase 9: Storage Isolation
192192

@@ -198,12 +198,11 @@
198198
- [ ] Access control: only runs within same orchestra can read each other's artifacts
199199

200200
## Acceptance Checklist
201-
- [ ] Phase 1: Result, secrets, artifacts all working with SDK support
202-
- [ ] Phase 2: Orchestra lifecycle, movements, director trigger, human choices
203-
- [ ] Phase 3: Chat with actions, real-time SSE
204-
- [ ] Phase 4: AI Director multi-model with fallback
205-
- [ ] Phase 5: Sub-orchestras with wait/parallel
206-
- [ ] Phase 6: Container executor on Docker Swarm
207-
- [ ] Phase 7: Timeout, budget, pause/resume
208-
- [ ] Phase 8: Full dashboard with score view and chat
201+
- [x] Phase 1: Result, secrets, artifacts all working with SDK support
202+
- [x] Phase 2: Orchestra lifecycle, movements, director trigger, human choices
203+
- [x] Phase 3: Chat with actions, real-time SSE
204+
- [x] Phase 4: AI Director multi-model with fallback
205+
- [x] Phase 5: Container executor on Docker Swarm
206+
- [x] Phase 7: Timeout, budget, pause/resume
207+
- [ ] Phase 8: Full dashboard with score view and chat (partial — secrets UI and infrastructure dashboard pending)
209208
- [ ] Phase 9: Storage isolation per orchestra/run

0 commit comments

Comments
 (0)