返回 DeepSeek-Reasonix
2026-06-29-autoresearch-runtime-design.md
根目录 / docs / superpowers / specs / 2026-06-29-autoresearch-runtime-design.md
1 # AutoResearch Runtime Design
2
3 ## Context
4
5 Reasonix already has Goal mode and AutoResearch instructions. When a goal looks
6 long-running, `activeGoalBlock` injects an AutoResearch protocol that asks the
7 model to create `.reasonix/autoresearch/<task-id>/` and maintain files such as
8 `task_spec.json`, `progress.json`, `findings.jsonl`, `directions_tried.json`, and
9 `heartbeat.jsonl`.
10
11 The current behavior is useful, but the durable state is mostly prompt-driven.
12 The host does not create the task directory, validate schemas, compute
13 `stale_count`, require pivots, expose a structured status API, or provide a real
14 resume mechanism. This design upgrades AutoResearch from a prompt convention to
15 a host-managed runtime.
16
17 ## Goals
18
19 - Host creates and owns the AutoResearch task id and directory layout.
20 - Host validates task state with typed schemas.
21 - Host records heartbeat, progress, findings, directions tried, and iteration log.
22 - Host computes stale and pivot pressure from accepted evidence and direction
23 repetition.
24 - Goal completion is blocked until required success criteria have evidence.
25 - Existing AutoResearch task ids can be resumed by the controller.
26 - The desktop/API layer can query AutoResearch status without parsing prompt text.
27
28 ## Non-Goals
29
30 - No large desktop panel in the first implementation.
31 - No autonomous background daemon. AutoResearch advances only through normal Goal
32 turns.
33 - No parallel writable sub-agent redesign in this feature.
34 - No network, publish, payment, credential, or destructive-operation bypass.
35 Existing Reasonix gates still apply.
36
37 ## Proposed Architecture
38
39 Add a new package:
40
41 ```text
42 internal/autoresearch/
43 task.go
44 store.go
45 schema.go
46 summary.go
47 readiness.go
48 ```
49
50 The package is responsible for all filesystem state under:
51
52 ```text
53 .reasonix/autoresearch/<task-id>/
54 state/
55 task_spec.json
56 progress.json
57 directions_tried.json
58 findings.jsonl
59 iteration_log.jsonl
60 logs/
61 heartbeat.jsonl
62 ```
63
64 The controller remains the owner of Goal lifecycle. AutoResearch state is a
65 durable sidecar attached to a running Goal when research mode is on or auto
66 research is triggered.
67
68 ## Core Types
69
70 `TaskSpec`:
71
72 ```json
73 {
74 "task_id": "20260629-153000-debug-lag",
75 "goal": "Find the root cause of UI event-loop lag and verify the fix",
76 "scope": ["desktop/frontend", "desktop"],
77 "non_goals": [],
78 "allowed_operations": {
79 "write": true,
80 "network": false,
81 "publish": false
82 },
83 "success_criteria": [
84 {
85 "id": "root_cause",
86 "description": "A reproducible root cause is identified",
87 "required": true,
88 "evidence_ids": []
89 }
90 ]
91 }
92 ```
93
94 `Progress`:
95
96 ```json
97 {
98 "status": "running",
99 "iteration": 4,
100 "current_direction": "profile markdown rendering",
101 "stale_count": 1,
102 "pivot_count": 0,
103 "blocked_reason": "",
104 "updated_at": "2026-06-29T15:30:00Z"
105 }
106 ```
107
108 `Finding` JSONL entries:
109
110 ```json
111 {
112 "id": "f1",
113 "kind": "test",
114 "summary": "A markdown render benchmark reproduces the lag",
115 "source": "command",
116 "command": "pnpm --dir desktop/frontend test",
117 "paths": ["desktop/frontend/src/components/MarkdownRenderer.tsx"],
118 "accepted": true,
119 "created_at": "2026-06-29T15:30:00Z"
120 }
121 ```
122
123 `DirectionTried` entries record normalized direction fingerprints so the host can
124 detect repeated work:
125
126 ```json
127 {
128 "fingerprint": "profile-markdown-rendering",
129 "summary": "Profile markdown rendering",
130 "first_seen_iteration": 2,
131 "last_seen_iteration": 4,
132 "count": 2
133 }
134 ```
135
136 ## Store API
137
138 The first implementation should expose a small host API:
139
140 ```go
141 type Store struct { /* workspace root + autoresearch root */ }
142
143 func (s *Store) CreateTask(goal string, opts CreateOptions) (*Task, error)
144 func (s *Store) LoadTask(taskID string) (*Task, error)
145 func (s *Store) ResumeFromGoalText(goal string) (*Task, bool, error)
146 func (s *Store) AppendHeartbeat(taskID string, h Heartbeat) error
147 func (s *Store) AppendFinding(taskID string, f Finding) error
148 func (s *Store) RecordDirection(taskID string, d Direction) (*Progress, error)
149 func (s *Store) UpdateProgress(taskID string, patch ProgressPatch) (*Progress, error)
150 func (s *Store) ValidateTask(taskID string) (*ValidationReport, error)
151 func (s *Store) Readiness(taskID string) (*ReadinessReport, error)
152 func (s *Store) Summary(taskID string) (*Summary, error)
153 ```
154
155 All writes should be atomic: write to a temp file in the same directory, fsync
156 where practical, then rename. JSONL appends should validate each entry before
157 writing.
158
159 ## Controller Integration
160
161 When Goal mode starts:
162
163 1. If research mode is off, behavior is unchanged.
164 2. If AutoResearch is on, the controller creates a task unless the goal contains
165 an explicit `.reasonix/autoresearch/<task-id>/` path.
166 3. If an explicit task path exists, the controller loads and validates that task.
167 4. The active goal state stores `AutoResearchTaskID`.
168
169 Before each AutoResearch turn:
170
171 - The controller appends a heartbeat with `status=starting_turn`.
172 - The composed user input includes a concise host-generated summary:
173 task id, status, iteration, current direction, stale count, pivot count,
174 blockers, open success criteria, and next required runtime action.
175 - The static prompt still explains the protocol, but host state is authoritative.
176
177 After each turn:
178
179 - The controller appends a heartbeat with `status=turn_done`.
180 - If tools ran, it records basic iteration metadata.
181 - If no accepted evidence was recorded, or the same direction repeated, the host
182 increments `stale_count`.
183 - At `stale_count >= 2`, the next turn summary requires a structural pivot.
184 - At `stale_count >= 4`, the goal is blocked unless the agent asks for the
185 smallest external input needed.
186
187 The model may still write detailed notes, but host-owned JSON files are the
188 source of truth.
189
190 ## Completion Gate
191
192 When the model emits `[goal:complete]`, the controller runs AutoResearch
193 readiness before normal Goal completion:
194
195 - `task_spec.json` and `progress.json` must exist and validate.
196 - Every required success criterion must have at least one accepted evidence id.
197 - Evidence ids must resolve to entries in `findings.jsonl`.
198 - If code was changed, there must be accepted verification evidence or an
199 explicit accepted reason why verification could not run.
200 - If `progress.status` is blocked, completion is rejected.
201 - If `stale_count > 0`, completion is allowed only when the final iteration added
202 accepted evidence that addresses the stale direction.
203
204 Failure returns a concrete intercept message to the model listing missing
205 criteria and required next actions.
206
207 ## Resume Behavior
208
209 AutoResearch resume has two paths:
210
211 - Explicit: a goal or prompt includes `.reasonix/autoresearch/<task-id>/`.
212 - Session-sidecar: the persisted Goal state contains `AutoResearchTaskID`.
213
214 On resume, the host validates the task. If state is corrupt, it blocks execution
215 with a repair message instead of silently asking the model to infer state.
216
217 ## Desktop/API Surface
218
219 The first UI implementation should make AutoResearch visible without turning the
220 chat surface into a project-management app. It should use the existing desktop
221 layout patterns: status bar for compact state, side panels for inspectable
222 details, and transcript cards for turn-local events.
223
224 Host methods:
225
226 - `AutoResearchStatus(taskID string)`
227 - `AutoResearchList()`
228 - `AutoResearchCurrent()`
229 - `AutoResearchFindings(taskID string, limit int)`
230 - `AutoResearchOpenTask(taskID string)`
231
232 The status payload should include:
233
234 - task id
235 - goal
236 - status
237 - iteration
238 - current direction
239 - stale count
240 - pivot count
241 - last heartbeat
242 - finding count
243 - open success criteria
244 - blocker
245
246 `AutoResearchOpenTask` opens `.reasonix/autoresearch/<task-id>/` in the
247 workspace panel or OS file browser, matching existing workspace reveal behavior.
248
249 ## Deferred Desktop UI Design
250
251 The runtime PR exposes the desktop API and compact tab metadata first. The
252 default frontend tool/status surface intentionally stays unchanged until the UI
253 entry points below are implemented and reviewed as a separate product decision.
254
255 ### Entry Points
256
257 AutoResearch should appear in three places:
258
259 1. Status bar chip: a compact always-visible indicator when the active tab has a
260 running or resumable AutoResearch task.
261 2. Context/side panel section: an inspectable task summary for the active tab.
262 3. Transcript event cards: lightweight markers for task creation, pivot required,
263 blocked, resumed, and completed.
264
265 This keeps the primary chat workflow intact while making durable research state
266 visible and recoverable.
267
268 ### Status Bar Chip
269
270 Add an `autoresearch` status item to the existing status bar item system. It is
271 hidden when no AutoResearch task is active for the current tab.
272
273 Display states:
274
275 - `Research 4` for running iteration 4.
276 - `Pivot` when `pivot_required` is true.
277 - `Blocked` when status is blocked.
278 - `Done` briefly after completion.
279
280 The chip should include an icon, short label, and tooltip. The tooltip contains:
281
282 - task id
283 - current direction
284 - stale count
285 - open criteria count
286 - last heartbeat age
287
288 Clicking the chip opens the AutoResearch detail panel.
289
290 ### Detail Panel
291
292 Add a compact AutoResearch section to the right-side context/workspace area. The
293 panel should be dense and operational, not decorative.
294
295 Header:
296
297 - task id
298 - status
299 - iteration
300 - last heartbeat
301 - open task folder button
302
303 Summary rows:
304
305 - goal
306 - current direction
307 - stale count
308 - pivot count
309 - blocker
310
311 Success criteria list:
312
313 - criterion description
314 - required/optional marker
315 - evidence count
316 - status: open, satisfied, blocked
317
318 Findings list:
319
320 - newest accepted findings first
321 - kind badge: command, file, test, benchmark, manual, review
322 - summary
323 - source command/path if present
324 - created time
325
326 Controls:
327
328 - Resume: starts or continues `/goal --research .reasonix/autoresearch/<task-id>/`
329 for the active tab when not running.
330 - Pause: clears active Goal continuation without deleting task state.
331 - Open Folder: reveals the task directory.
332 - Copy Task ID: copies the task id.
333
334 The first implementation can omit inline editing of task spec fields. Task state
335 is owned by the host and model workflow; UI edits would need validation and a
336 separate audit path.
337
338 ### Transcript Cards
339
340 Emit lightweight notices or typed events for important AutoResearch lifecycle
341 changes:
342
343 - task created
344 - task resumed
345 - heartbeat failed
346 - pivot required
347 - readiness blocked completion
348 - task completed
349 - task blocked
350
351 The transcript should not render the full runtime summary every turn. It should
352 show only meaningful lifecycle changes, because the full summary is already
353 available in the detail panel and injecting it visually every turn would add
354 noise.
355
356 ### Frontend State Flow
357
358 Extend bridge/types with:
359
360 ```ts
361 interface AutoResearchStatusView {
362 taskId: string;
363 goal: string;
364 status: "running" | "blocked" | "complete" | "stopped" | "invalid";
365 iteration: number;
366 currentDirection: string;
367 staleCount: number;
368 pivotCount: number;
369 pivotRequired: boolean;
370 lastHeartbeatAt: string;
371 findingCount: number;
372 openCriteria: AutoResearchCriterionView[];
373 blocker: string;
374 taskPath: string;
375 }
376 ```
377
378 `MetaForTab` should include only the small active-task summary needed to render
379 the status chip. The heavier findings list should be loaded on demand through
380 `AutoResearchFindings`, so normal chat turns do not pull large JSONL data into
381 frontend state.
382
383 Refresh strategy:
384
385 - Refresh current AutoResearch status after `turn_done`.
386 - Refresh when switching tabs.
387 - Refresh when receiving an AutoResearch lifecycle event.
388 - Do not poll every second. The heartbeat is persisted for durability, not for a
389 live dashboard animation.
390
391 ### Empty and Error States
392
393 - No active task: hide the chip and show no panel section by default.
394 - Invalid state: show an `Invalid` status with the exact validation error and an
395 Open Folder action.
396 - Missing task folder on resume: show a blocked state and keep the Goal from
397 silently continuing.
398 - Stale state: show the pivot requirement prominently but do not mark it as an
399 error.
400
401 ### Accessibility and Layout
402
403 - Use existing button, tooltip, panel, and status bar patterns.
404 - Keep the status chip width stable so the status bar does not shift during
405 streaming.
406 - Long goals and directions should wrap in the panel but truncate in the chip
407 tooltip label.
408 - Findings should be keyboard navigable and copyable.
409 - Use icons only where the existing design system already uses them; avoid a
410 marketing-style card layout.
411
412 ## Error Handling
413
414 - Missing task directory: create only when starting a new task; otherwise return
415 a clear resume error.
416 - Corrupt JSON: block AutoResearch continuation and surface the exact file.
417 - Schema mismatch: return validation errors with paths and fields.
418 - Failed heartbeat write: warn and continue for non-critical writes, but block
419 completion if state cannot be validated.
420 - Concurrent writes: serialize per task id inside the store.
421
422 ## Testing
423
424 Unit tests:
425
426 - task id generation is stable in shape and collision-safe
427 - store creates the expected directory layout
428 - schema validation rejects missing required fields
429 - JSONL append rejects invalid entries
430 - direction repetition increments stale count
431 - pivot threshold produces a pivot requirement
432 - readiness blocks missing evidence
433 - readiness accepts complete criteria with accepted findings
434 - resume loads explicit task id from goal text
435
436 Controller tests:
437
438 - AutoResearch goal creates task state
439 - active goal block includes host-generated summary
440 - every turn appends heartbeat
441 - `[goal:complete]` is intercepted when readiness fails
442 - explicit `.reasonix/autoresearch/<task-id>/` resumes existing state
443
444 Desktop/API tests:
445
446 - app method returns current AutoResearch status for the active tab
447 - tab metadata includes only compact AutoResearch summary
448 - findings are loaded on demand and capped by limit
449 - status chip hides when no task is active
450 - status chip opens the detail panel
451 - detail panel renders running, pivot, blocked, invalid, and complete states
452 - Open Folder calls the existing reveal/open path behavior
453 - tab switch refreshes the visible AutoResearch summary
454
455 ## Rollout
456
457 Phase 1: implement `internal/autoresearch` store, schemas, summary, readiness,
458 and tests.
459
460 Phase 2: integrate with Goal controller create/resume/heartbeat/summary and
461 completion intercept.
462
463 Phase 3: add desktop/API status methods, status bar chip, detail panel, transcript
464 lifecycle cards, and frontend tests.
465
466 Phase 4: optionally let tools or a dedicated host tool record structured
467 findings directly, reducing reliance on model-authored JSON.
468
469 ## Compatibility and Cache Impact
470
471 This design should not change provider-visible tool schemas in phase 1. The
472 active goal prompt changes only when AutoResearch is active. Cache impact is
473 therefore low for ordinary sessions and medium for AutoResearch sessions because
474 the injected runtime summary changes each turn.
475
476 No existing `.reasonix/autoresearch` task should be deleted or rewritten without
477 validation and explicit migration logic.
478
478 lines MARKDOWN