CallMeDaniel Claude Opus 4.6 (1M context) commited on
Commit
4ebb6fc
·
1 Parent(s): 9ff7a7c

docs: add memory, planning, and collaboration design spec

Browse files

Covers pre-integration refactoring (dead code removal, history rendering,
part name extraction, config) plus CrewAI Memory recall/remember,
planning on Crews, and collaboration via allow_delegation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

docs/superpowers/specs/2026-04-12-memory-planning-collab-design.md ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CrewAI Memory, Planning & Collaboration — Design Spec
2
+
3
+ **Date:** 2026-04-12
4
+ **Scope:** Pre-integration refactoring + adding CrewAI memory (cross-turn recall), planning (step-by-step coordination), and collaboration (agent delegation) to the existing AgentDispatchFlow.
5
+
6
+ ## Problem
7
+
8
+ 1. Agents have no memory — each turn is stateless. `_build_agent_context()` dumps raw chat history as text into every agent's prompt, scaling linearly with conversation length. Agents repeat prior advice because they can't semantically recall relevant context.
9
+
10
+ 2. No planning — agents respond independently without coordinated step-by-step plans per turn.
11
+
12
+ 3. No collaboration — agents can't ask each other questions mid-turn (`allow_delegation=False`).
13
+
14
+ 4. Dead code in `agents/prompts.py` — 4 functions orphaned after Flow refactor. Part name derivation duplicated in two files.
15
+
16
+ ## Decision
17
+
18
+ **Phase 1 (this spec):** Refactor dead code, remove raw history, add config, then integrate CrewAI Memory + Planning + Collaboration into the existing AgentDispatchFlow.
19
+
20
+ ## Phase 1A: Pre-Integration Refactoring
21
+
22
+ ### 1. Clean up `agents/prompts.py`
23
+
24
+ Remove dead functions:
25
+ - `build_orchestrator_system_prompt()` — not called in CrewAI flow
26
+ - `build_chat_messages()` — not called in CrewAI flow
27
+ - `parse_orchestrator_response()` — not called in CrewAI flow
28
+ - `route_by_keywords()` — duplicated by `agent_flow.route_agents()`
29
+
30
+ Also remove module-level constants only used by dead functions:
31
+ - `_ROUTING_KEYWORDS`
32
+ - `CAD_TRIGGER_KEYWORDS`
33
+
34
+ Keep only `parse_mentions()` (used by `server/routes.py` and `server/mcp.py`).
35
+
36
+ ### 2. Remove raw history from `_build_agent_context()`
37
+
38
+ In `agents/crew_orchestrator.py`, change `_build_agent_context()` to render only:
39
+ - DesignState spec (or approved plan)
40
+ - User's latest message
41
+
42
+ Remove the `## Recent conversation` block that renders `history[-max_history:]` as text. Memory recall will replace this.
43
+
44
+ The `history` parameter stays in the function signature (used by `extract_decisions()` downstream) but is no longer rendered into agent context.
45
+
46
+ ### 3. Extract part name derivation
47
+
48
+ Create `core/utils.py` with:
49
+
50
+ ```python
51
+ def derive_part_name(text: str, max_chars: int = 40) -> str:
52
+ """Derive a filesystem-safe part name from text."""
53
+ name = text[:max_chars].strip().replace(" ", "_").lower()
54
+ name = "".join(c for c in name if c.isalnum() or c == "_")
55
+ return name or "part"
56
+ ```
57
+
58
+ Replace duplicated logic in:
59
+ - `agents/crew_orchestrator.py:208-209`
60
+ - `agents/orchestrator.py:100-104`
61
+
62
+ ### 4. Add memory/planning/collaboration config
63
+
64
+ Add to `config.yaml`:
65
+
66
+ ```yaml
67
+ memory:
68
+ enabled: true
69
+ embedder_provider: google-generativeai
70
+ embedder_model: gemini-embedding-001
71
+ recency_weight: 0.4
72
+ semantic_weight: 0.4
73
+ importance_weight: 0.2
74
+ recency_half_life_days: 1
75
+ recall_limit: 5
76
+ recall_depth: shallow
77
+
78
+ crew:
79
+ planning: true
80
+ collaboration: true
81
+ ```
82
+
83
+ Add to `config/settings.py`:
84
+
85
+ ```python
86
+ class MemoryConfig(BaseModel):
87
+ enabled: bool = True
88
+ embedder_provider: str = "google-generativeai"
89
+ embedder_model: str = "gemini-embedding-001"
90
+ recency_weight: float = 0.4
91
+ semantic_weight: float = 0.4
92
+ importance_weight: float = 0.2
93
+ recency_half_life_days: float = 1.0
94
+ recall_limit: int = 5
95
+ recall_depth: str = "shallow"
96
+
97
+ class CrewConfig(BaseModel):
98
+ planning: bool = True
99
+ collaboration: bool = True
100
+ ```
101
+
102
+ ## Phase 1B: Memory Integration
103
+
104
+ ### Memory lifecycle
105
+
106
+ `CrewOrchestrator.__init__()` creates a `Memory` instance if `settings.memory.enabled`:
107
+
108
+ ```python
109
+ from crewai import Memory
110
+
111
+ self._memory = Memory(
112
+ storage=str(self.output_dir / ".memory"),
113
+ embedder={
114
+ "provider": settings.memory.embedder_provider,
115
+ "config": {"model_name": settings.memory.embedder_model},
116
+ },
117
+ recency_weight=settings.memory.recency_weight,
118
+ semantic_weight=settings.memory.semantic_weight,
119
+ importance_weight=settings.memory.importance_weight,
120
+ recency_half_life_days=settings.memory.recency_half_life_days,
121
+ ) if settings.memory.enabled else None
122
+ ```
123
+
124
+ ### Flow integration
125
+
126
+ `AgentFlowState` gets a new optional field to carry the memory reference:
127
+
128
+ ```python
129
+ class AgentFlowState(BaseModel):
130
+ ...
131
+ # Memory reference (not serialized, set by orchestrator)
132
+ class Config:
133
+ arbitrary_types_allowed = True
134
+ ```
135
+
136
+ The memory instance is passed as an instance attribute on the Flow, not via state (Memory isn't serializable). The orchestrator sets it before kickoff:
137
+
138
+ ```python
139
+ flow = AgentDispatchFlow(initial_state=AgentFlowState(...))
140
+ flow._memory = self._memory # Instance attr, not state field
141
+ flow.kickoff()
142
+ ```
143
+
144
+ ### Recall — before each agent
145
+
146
+ In `_build_crew_agent()`, replace the raw history context with recalled memories:
147
+
148
+ ```python
149
+ def _recall_for_agent(self, agent_id: str) -> str:
150
+ if self._memory is None:
151
+ return ""
152
+ matches = self._memory.recall(
153
+ self.state.message,
154
+ scope=f"/agent/{agent_id}",
155
+ limit=settings.memory.recall_limit,
156
+ depth=settings.memory.recall_depth,
157
+ )
158
+ if not matches:
159
+ return ""
160
+ lines = [f"- {m.record.content}" for m in matches]
161
+ return "## Relevant context from prior turns\n" + "\n".join(lines)
162
+ ```
163
+
164
+ Injected into task description:
165
+
166
+ ```python
167
+ memories = self._recall_for_agent(agent_id)
168
+ task_description = (
169
+ f"{self.state.context}\n\n" # DesignState + user message
170
+ f"{memories}\n\n" # Recalled memories
171
+ f"As the {agent_def.role}, ..."
172
+ )
173
+ ```
174
+
175
+ ### Remember — after each agent
176
+
177
+ In `_run_advisor_crew()`, after parsing responses:
178
+
179
+ ```python
180
+ for i, agent_id in enumerate(advisor_ids):
181
+ ...
182
+ if raw.strip():
183
+ responses.append(AgentResponse.from_agent(agent_id, raw.strip()))
184
+ self._remember_response(agent_id, raw.strip())
185
+ ```
186
+
187
+ In `_run_cad_step()` and `_run_cam_step()`, after setting response:
188
+
189
+ ```python
190
+ self._remember_response("cad", raw_output)
191
+ self._remember_response("cam", raw_output)
192
+ ```
193
+
194
+ Helper:
195
+
196
+ ```python
197
+ def _remember_response(self, agent_id: str, content: str):
198
+ if self._memory is None:
199
+ return
200
+ self._memory.remember(content, scope=f"/agent/{agent_id}")
201
+ ```
202
+
203
+ ### Agent scopes
204
+
205
+ ```
206
+ /agent/design — Design Agent memories
207
+ /agent/engineering — Engineering Agent memories
208
+ /agent/cnc — CNC Agent memories
209
+ /agent/cad — CAD Coder memories
210
+ /agent/cam — CAM Agent memories
211
+ ```
212
+
213
+ ## Phase 1C: Planning Integration
214
+
215
+ Enable planning on all Crews. The AgentPlanner generates step-by-step instructions injected into task descriptions before execution.
216
+
217
+ In `_run_advisor_crew()`:
218
+
219
+ ```python
220
+ crew = Crew(
221
+ agents=crew_agents,
222
+ tasks=crew_tasks,
223
+ process=Process.sequential,
224
+ planning=settings.crew.planning,
225
+ planning_llm=self._build_llm(),
226
+ verbose=False,
227
+ )
228
+ ```
229
+
230
+ In `_run_single_agent_crew()`:
231
+
232
+ ```python
233
+ crew = Crew(
234
+ agents=[crew_agent],
235
+ tasks=[task],
236
+ process=Process.sequential,
237
+ planning=settings.crew.planning,
238
+ planning_llm=self._build_llm(),
239
+ verbose=False,
240
+ )
241
+ ```
242
+
243
+ ## Phase 1D: Collaboration Integration
244
+
245
+ Enable delegation on advisory agents so they can ask each other questions within the advisor Crew. CAD and CAM keep `allow_delegation=False` (single-agent Crews).
246
+
247
+ In `_build_crew_agent()`:
248
+
249
+ ```python
250
+ crew_agent = Agent(
251
+ ...
252
+ allow_delegation=settings.crew.collaboration and agent_id in ADVISOR_IDS,
253
+ ...
254
+ )
255
+ ```
256
+
257
+ ## What Changes
258
+
259
+ | File | Change |
260
+ |------|--------|
261
+ | `agents/prompts.py` | Remove 4 dead functions + dead constants. Keep `parse_mentions()` only. |
262
+ | `agents/crew_orchestrator.py` | Remove history rendering from `_build_agent_context()`. Use `derive_part_name()`. Create `Memory` in `__init__()`. Pass memory to Flow. |
263
+ | `agents/agent_flow.py` | Add `_memory` attr. Add `_recall_for_agent()`, `_remember_response()`. Update `_build_crew_agent()` for delegation. Update Crew creation for planning. Inject memories into task descriptions. |
264
+ | `agents/orchestrator.py` | Use `derive_part_name()` in `_execute_cad_code()`. |
265
+ | `core/utils.py` | **NEW** — `derive_part_name()` |
266
+ | `config.yaml` | Add `memory` and `crew` sections. |
267
+ | `config/settings.py` | Add `MemoryConfig`, `CrewConfig` models. |
268
+
269
+ ## What Does NOT Change
270
+
271
+ | File | Reason |
272
+ |------|--------|
273
+ | `agents/base.py` | Interface unchanged |
274
+ | `agents/design_state.py` | Domain model, not orchestration |
275
+ | `agents/tools.py` | Tools unchanged |
276
+ | `agents/gap_analyzer.py` | Runs after Flow |
277
+ | `agents/definitions.py` | Agent metadata unchanged |
278
+ | `server/routes.py` | API contract unchanged |
279
+ | `web/index.html` | Frontend unchanged |
280
+
281
+ ## Testing
282
+
283
+ **New tests:**
284
+ - `TestDerivePartName` — empty, special chars, truncation
285
+ - `TestRecallForAgent` — returns formatted memories, empty when no memory
286
+ - `TestRememberResponse` — stores with correct scope, no-op when memory is None
287
+ - `TestPlanningEnabled` — Crew created with `planning=True`
288
+ - `TestCollaborationEnabled` — advisors have `allow_delegation=True`, generators have `False`
289
+
290
+ **Modified tests:**
291
+ - `test_crew_orchestrator.py` — update any tests relying on raw history in context
292
+ - `test_agent_flow.py` — update mocks for memory/planning/collaboration
293
+
294
+ **Existing tests:**
295
+ - Routing tests unchanged
296
+ - Gap analysis tests unchanged
297
+ - Design state tests unchanged