feat(tasks): analyst opens the task; freestyle threads it into implementation

Two coupled gaps from tracing a real run:

1. Freestyle implements in phase 2 via stages compiled from the architect's
   execution_plan (ExecutionPlanCompiler sets allowedTools = stage.tools), so the
   static allow-lists never reach it and architect_freestyle.md banned every tool
   but the file four. Teach the architect to thread an analysis-referenced task
   through the plan: implementing stages get task_context/task_update and claim +
   submit_for_review; the final/review stage completes it. No task referenced → no
   task tools, and the plan never creates one.

2. Give the analyst task_create so the work is framed as a tracked item up front
   (role_pipeline + freestyle). "Read-only" for the analyst means it writes no
   files; a task is an event-log entry, not a file write — task_create is T2, so
   opening one is approval-gated. The analyst names the new id in the analysis so
   the implementer claims it and the reviewer completes it; the implementer now
   creates only as a fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 15:13:52 +00:00
parent de54be7ecb
commit 12d5b9d7dc
6 changed files with 37 additions and 17 deletions
+5 -3
View File
@@ -1,13 +1,15 @@
id = "freestyle_planning" id = "freestyle_planning"
start = "analyst" start = "analyst"
# analyst is read-only, so it gets only the read-only task tools: task_search to find related # analyst writes no files, but it owns task framing: task_search/task_context (read-only) find
# or duplicate work and task_context to ground the analysis in an existing task. # existing work, and task_create (T2, approval-gated — a task is an event-log entry, not a file
# write) opens a task for this work and names its id in the analysis, so the architect can thread
# it into the plan's implementation stages.
[[stages]] [[stages]]
id = "analyst" id = "analyst"
prompt = "prompts/analyst_freestyle.md" prompt = "prompts/analyst_freestyle.md"
produces = [{ name = "analysis", kind = "analysis" }] produces = [{ name = "analysis", kind = "analysis" }]
allowed_tools = ["file_read", "ShellTool", "task_search", "task_context"] allowed_tools = ["file_read", "ShellTool", "task_search", "task_context", "task_create"]
token_budget = 16384 token_budget = 16384
max_retries = 2 max_retries = 2
+3 -1
View File
@@ -10,7 +10,9 @@ Steps:
code. Identify the files, modules, and subsystems involved. Do not modify anything. code. Identify the files, modules, and subsystems involved. Do not modify anything.
3. Check for existing work: `task_search` for related, duplicate, or blocking tasks, and 3. Check for existing work: `task_search` for related, duplicate, or blocking tasks, and
`task_context` to load any the request names. Fold what you find into the analysis rather `task_context` to load any the request names. Fold what you find into the analysis rather
than re-deriving it; flag a duplicate instead of restating it. than re-deriving it; flag a duplicate instead of restating it. If this work warrants tracking
(per the task policy) and no task covers it, `task_create` one and name its id in the analysis
so the implementer claims it and the reviewer completes it.
4. Derive concrete, checkable requirements and acceptance criteria. 4. Derive concrete, checkable requirements and acceptance criteria.
The decision history above (steering, approvals, prior verdicts) is ground truth — honour it. The decision history above (steering, approvals, prior verdicts) is ground truth — honour it.
@@ -4,7 +4,10 @@ given a directory path), `ls`, `grep`, `cat`, `find`.
Before deriving requirements, check for existing work: `task_search` for related, duplicate, or Before deriving requirements, check for existing work: `task_search` for related, duplicate, or
blocking tasks and `task_context` to load any the goal names. Fold what you find into the blocking tasks and `task_context` to load any the goal names. Fold what you find into the
analysis rather than re-deriving it; flag a duplicate instead of restating it. analysis rather than re-deriving it; flag a duplicate instead of restating it. If a task already
covers this work, name its id (e.g. `auth-142`) in the analysis; if none does and the work
warrants tracking (per the task policy), `task_create` one and name its id — either way later
stages thread it through the plan.
Emit the `analysis` artifact (JSON, schema provided): Emit the `analysis` artifact (JSON, schema provided):
- `summary`: the goal in your own words. - `summary`: the goal in your own words.
@@ -57,9 +57,21 @@ Emit a JSON object that validates against the `execution_plan` schema:
llm-emitted kind. llm-emitted kind.
- Declare `needs`: every upstream artifact id the stage's prompt references. Every id in - Declare `needs`: every upstream artifact id the stage's prompt references. Every id in
`needs` must be `produces`d by a strictly earlier stage. `needs` must be `produces`d by a strictly earlier stage.
- Include `tools` only for stages that write or edit files: - Include `tools` per stage as it needs them, using only names from this set:
`["file_read", "file_write", "file_edit", "ShellTool"]`. Do not invent tool names `file_read`, `file_write`, `file_edit`, `ShellTool`, `task_context`, `task_update`,
beyond this set. `task_search`. Stages that write or edit files take the file set
(`["file_read", "file_write", "file_edit", "ShellTool"]`). Do not invent names beyond
this set.
- **Task tracking — only if the `analysis` references a task** (an id like `auth-142` that
the analyst found or opened with `task_create`; if none is referenced there is no task to
track). When one is referenced, thread it through the plan so the work stays tracked:
- Give the stage that does the work `task_context` and `task_update`, and have its
`prompt` `task_update action=claim` the task before starting and
`action=submit_for_review` when its output is ready.
- Give the final or review stage `task_context` and `task_update`, and have its `prompt`
`task_update action=complete` the task once the work is accepted.
- If the `analysis` references no task, omit the task tools entirely. Do not create a
new task here — creation is out of scope for the plan.
- Keep stages small and single-responsibility. Prefer more stages over large monolithic - Keep stages small and single-responsibility. Prefer more stages over large monolithic
prompts. prompts.
+3 -3
View File
@@ -4,9 +4,9 @@ You receive the `impl_plan` artifact (above). Execute it using the tools availab
(`file_read`, `file_write`, `file_edit`, and shell). File writes land in the bound workspace. (`file_read`, `file_write`, `file_edit`, and shell). File writes land in the bound workspace.
Steps: Steps:
1. If this work is tracked as a task — or warrants it per the task policy in the context above — 1. If the analysis opened or referenced a task, `task_context` to load it and `task_update
`task_context` to load it and `task_update action=claim` before you start; `task_create` one action=claim` before you start (`task_create` one only if the work warrants tracking and none
if none exists. Skip this for a self-contained change. exists). Skip this for a self-contained change.
2. Work through the plan `steps` in order. Read before you edit. 2. Work through the plan `steps` in order. Read before you edit.
3. Make the change with `file_write` / `file_edit`. Keep new code consistent with the 3. Make the change with `file_write` / `file_edit`. Keep new code consistent with the
surrounding style, naming, and patterns. surrounding style, naming, and patterns.
+7 -6
View File
@@ -24,16 +24,17 @@
id = "role_pipeline" id = "role_pipeline"
start = "analyst" start = "analyst"
# 1. Understand the request and the relevant code. Read-only. # 1. Understand the request and the relevant code — writes no files (ground_references checks
# ground_references: every workspace-relative file path the analysis names is checked # every workspace path the analysis names; a hallucinated path fails the stage and retries).
# for existence; a hallucinated path fails the stage and retries with the misses fed back. # It also owns task framing: task_search/task_context (read-only) find existing work, and
# task_search/task_context (read-only) let it find related or duplicate tasks and load # task_create (T2, approval-gated — a task is an event-log entry, not a file write) opens a
# their context, so the analysis is grounded in existing work rather than re-derived. # task for this work up front, named in the analysis so the implementer claims it and the
# reviewer completes it.
[[stages]] [[stages]]
id = "analyst" id = "analyst"
prompt = "prompts/analyst.md" prompt = "prompts/analyst.md"
produces = [{ name = "analysis", kind = "analysis" }] produces = [{ name = "analysis", kind = "analysis" }]
allowed_tools = ["file_read", "ShellTool", "task_search", "task_context"] allowed_tools = ["file_read", "ShellTool", "task_search", "task_context", "task_create"]
ground_references = true ground_references = true
token_budget = 16384 token_budget = 16384
max_retries = 2 max_retries = 2