Skip to content

Commit c4bbca2

Browse files
committed
refactor: remove experimental.task_system feature flag — task system is always on
Remove the task_system feature flag and all conditional branches, making the task system unconditionally active. Strip taskSystemEnabled parameter threading from 27 files (~580 lines removed). Add explicit guardrail in Loom prompt to prevent direct plan execution (root cause of checkbox regression). Remove dead todo finalization safety-net code from plugin interface. Update all tests — 1371 pass, 0 fail.
1 parent 9b1965a commit c4bbca2

28 files changed

Lines changed: 512 additions & 734 deletions

.weave/plans/task-system-cleanup.md

Lines changed: 360 additions & 0 deletions
Large diffs are not rendered by default.

src/agents/builtin-agents.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ export interface CreateBuiltinAgentsOptions {
2828
fingerprint?: ProjectFingerprint | null
2929
/** Custom agent metadata for Loom's dynamic delegation prompt */
3030
customAgentMetadata?: AvailableAgent[]
31-
/** Whether the atomic task system is enabled (controls prompt content) */
32-
taskSystemEnabled?: boolean
3331
}
3432

3533
const AGENT_FACTORIES: Record<WeaveAgentName, AgentFactory> = {
@@ -183,7 +181,6 @@ export function createBuiltinAgents(options: CreateBuiltinAgentsOptions = {}): R
183181
resolveSkills,
184182
fingerprint,
185183
customAgentMetadata,
186-
taskSystemEnabled,
187184
} = options
188185

189186
const disabledSet = new Set(disabledAgents)
@@ -208,9 +205,9 @@ export function createBuiltinAgents(options: CreateBuiltinAgentsOptions = {}): R
208205
// so their prompts conditionally omit references to disabled agents
209206
let built: AgentConfig
210207
if (name === "loom") {
211-
built = createLoomAgentWithOptions(resolvedModel, disabledSet, fingerprint, customAgentMetadata, taskSystemEnabled)
208+
built = createLoomAgentWithOptions(resolvedModel, disabledSet, fingerprint, customAgentMetadata)
212209
} else if (name === "tapestry") {
213-
built = createTapestryAgentWithOptions(resolvedModel, disabledSet, taskSystemEnabled)
210+
built = createTapestryAgentWithOptions(resolvedModel, disabledSet)
214211
} else {
215212
built = buildAgent(factory, resolvedModel, {
216213
categories,

src/agents/loom/index.test.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,4 @@ describe("createLoomAgentWithOptions", () => {
223223
}])
224224
expect(config.mode).toBe("primary")
225225
})
226-
227-
it("uses task system prompt when taskSystemEnabled is true", () => {
228-
const config = createLoomAgentWithOptions("claude-opus-4", undefined, null, undefined, true)
229-
expect(config.prompt).toContain("TASK TRACKING")
230-
expect(config.prompt).toContain("task_create")
231-
expect(config.prompt).not.toContain("TODO OBSESSION")
232-
})
233-
234-
it("returns composed prompt (not default) when only taskSystemEnabled is set", () => {
235-
const config = createLoomAgentWithOptions("claude-opus-4", undefined, null, undefined, true)
236-
// Should NOT fall through to LOOM_DEFAULTS since taskSystemEnabled triggers composition
237-
expect(config.prompt).toContain("task_update")
238-
})
239226
})

src/agents/loom/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@ export function createLoomAgentWithOptions(
1616
disabledAgents?: Set<string>,
1717
fingerprint?: ProjectFingerprint | null,
1818
customAgents?: AvailableAgent[],
19-
taskSystemEnabled?: boolean,
2019
): AgentConfig {
21-
if ((!disabledAgents || disabledAgents.size === 0) && !fingerprint && (!customAgents || customAgents.length === 0) && !taskSystemEnabled) {
20+
if ((!disabledAgents || disabledAgents.size === 0) && !fingerprint && (!customAgents || customAgents.length === 0)) {
2221
return { ...LOOM_DEFAULTS, model, mode: "primary" }
2322
}
2423
return {
2524
...LOOM_DEFAULTS,
26-
prompt: composeLoomPrompt({ disabledAgents, fingerprint, customAgents, taskSystemEnabled }),
25+
prompt: composeLoomPrompt({ disabledAgents, fingerprint, customAgents }),
2726
model,
2827
mode: "primary",
2928
}

src/agents/loom/prompt-composer.test.ts

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ describe("composeLoomPrompt", () => {
5050
expect(prompt).toContain("Tapestry runs Weft and Warp")
5151
})
5252

53+
it("contains delegation guardrail in PlanWorkflow section", () => {
54+
const prompt = composeLoomPrompt()
55+
expect(prompt).toContain("NEVER execute plan tasks directly")
56+
})
57+
5358
it("does not include ProjectContext with no fingerprint", () => {
5459
const prompt = composeLoomPrompt()
5560
expect(prompt).not.toContain("<ProjectContext>")
@@ -134,6 +139,12 @@ describe("buildDelegationSection", () => {
134139
})
135140

136141
describe("buildPlanWorkflowSection", () => {
142+
it("contains delegation guardrail at the top", () => {
143+
const section = buildPlanWorkflowSection(new Set())
144+
expect(section).toContain("NEVER execute plan tasks directly")
145+
expect(section).toContain("/start-work")
146+
})
147+
137148
it("includes Pattern, Weft, Warp, and Tapestry by default", () => {
138149
const section = buildPlanWorkflowSection(new Set())
139150
expect(section).toContain("Pattern")
@@ -219,20 +230,30 @@ describe("individual section builders", () => {
219230
expect(buildRoleSection()).toContain("orchestrator")
220231
})
221232

222-
it("buildDisciplineSection contains TODO OBSESSION", () => {
223-
expect(buildDisciplineSection()).toContain("TODO OBSESSION")
233+
it("buildDisciplineSection contains TASK TRACKING", () => {
234+
expect(buildDisciplineSection()).toContain("TASK TRACKING")
235+
})
236+
237+
it("buildDisciplineSection contains task_create and task_update", () => {
238+
expect(buildDisciplineSection()).toContain("task_create")
239+
expect(buildDisciplineSection()).toContain("task_update")
240+
})
241+
242+
it("buildDisciplineSection contains plan delegation guardrail", () => {
243+
expect(buildDisciplineSection()).toContain("PLANS: Never execute plan tasks directly")
244+
expect(buildDisciplineSection()).toContain("/start-work")
224245
})
225246

226247
it("buildSidebarTodosSection contains format rules", () => {
227248
expect(buildSidebarTodosSection()).toContain("35 chars")
228249
})
229250

230-
it("buildSidebarTodosSection contains BEFORE FINISHING mandatory block", () => {
251+
it("buildSidebarTodosSection uses task_create and task_update", () => {
231252
const section = buildSidebarTodosSection()
232-
expect(section).toContain("BEFORE FINISHING (MANDATORY)")
233-
expect(section).toContain("NON-NEGOTIABLE")
234-
expect(section).toContain("final todowrite")
235-
expect(section).not.toContain("sidebar hides")
253+
expect(section).toContain("task_create")
254+
expect(section).toContain("task_update")
255+
expect(section).toContain("task_list")
256+
expect(section).toContain("atomically")
236257
})
237258

238259
it("buildDelegationNarrationSection contains duration hints", () => {
@@ -256,49 +277,6 @@ describe("individual section builders", () => {
256277
})
257278
})
258279

259-
describe("task system enabled — Loom prompt sections", () => {
260-
it("buildDisciplineSection uses task_create language when enabled", () => {
261-
const section = buildDisciplineSection(true)
262-
expect(section).toContain("TASK TRACKING")
263-
expect(section).toContain("task_create")
264-
expect(section).toContain("task_update")
265-
expect(section).not.toContain("TODO OBSESSION")
266-
expect(section).not.toContain("todowrite")
267-
})
268-
269-
it("buildSidebarTodosSection uses task tools when enabled", () => {
270-
const section = buildSidebarTodosSection(true)
271-
expect(section).toContain("task_create")
272-
expect(section).toContain("task_update")
273-
expect(section).toContain("task_list")
274-
expect(section).toContain("atomically")
275-
expect(section).not.toContain("todowrite")
276-
expect(section).not.toContain("BEFORE FINISHING")
277-
})
278-
279-
it("buildDelegationNarrationSection uses task tools when enabled", () => {
280-
const section = buildDelegationNarrationSection(new Set(), true)
281-
expect(section).toContain("task_create")
282-
expect(section).toContain("task_update")
283-
expect(section).not.toContain("todowrite")
284-
})
285-
286-
it("composeLoomPrompt with taskSystemEnabled replaces todowrite references", () => {
287-
const prompt = composeLoomPrompt({ taskSystemEnabled: true })
288-
expect(prompt).toContain("TASK TRACKING")
289-
expect(prompt).toContain("task_create")
290-
expect(prompt).not.toContain("TODO OBSESSION")
291-
expect(prompt).not.toContain("BEFORE FINISHING (MANDATORY)")
292-
})
293-
294-
it("composeLoomPrompt without taskSystemEnabled uses legacy todowrite", () => {
295-
const prompt = composeLoomPrompt()
296-
expect(prompt).toContain("TODO OBSESSION")
297-
expect(prompt).toContain("todowrite")
298-
expect(prompt).not.toContain("TASK TRACKING")
299-
})
300-
})
301-
302280
describe("buildCustomAgentDelegationSection", () => {
303281
const makeCustomAgent = (name: string, domain: string, trigger: string): AvailableAgent => ({
304282
name,

src/agents/loom/prompt-composer.ts

Lines changed: 17 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ export interface LoomPromptOptions {
1818
fingerprint?: ProjectFingerprint | null
1919
/** Custom agent metadata for dynamic delegation sections */
2020
customAgents?: AvailableAgent[]
21-
/** Whether the atomic task system is enabled (default false for backward compat) */
22-
taskSystemEnabled?: boolean
2321
}
2422

2523
export function buildRoleSection(): string {
@@ -30,32 +28,22 @@ You are the team lead. Understand the request, break it into tasks, delegate int
3028
</Role>`
3129
}
3230

33-
export function buildDisciplineSection(taskSystemEnabled = false): string {
34-
if (taskSystemEnabled) {
35-
return `<Discipline>
31+
export function buildDisciplineSection(): string {
32+
return `<Discipline>
3633
TASK TRACKING (NON-NEGOTIABLE):
3734
- 2+ steps → task_create FIRST, one task per step
3835
- task_update to in_progress before starting (ONE at a time)
3936
- task_update to completed IMMEDIATELY after each step
4037
- NEVER batch completions
4138
42-
No tasks on multi-step work = INCOMPLETE WORK.
43-
</Discipline>`
44-
}
45-
return `<Discipline>
46-
TODO OBSESSION (NON-NEGOTIABLE):
47-
- 2+ steps → todowrite FIRST, atomic breakdown
48-
- Mark in_progress before starting (ONE at a time)
49-
- Mark completed IMMEDIATELY after each step
50-
- NEVER batch completions
39+
PLANS: Never execute plan tasks directly — always /start-work → Tapestry.
5140
52-
No todos on multi-step work = INCOMPLETE WORK.
41+
No tasks on multi-step work = INCOMPLETE WORK.
5342
</Discipline>`
5443
}
5544

56-
export function buildSidebarTodosSection(taskSystemEnabled = false): string {
57-
if (taskSystemEnabled) {
58-
return `<SidebarTodos>
45+
export function buildSidebarTodosSection(): string {
46+
return `<SidebarTodos>
5947
The user sees a Todo sidebar (~35 char width). Use task_create and task_update to manage it:
6048
6149
- task_create: creates a task and syncs to sidebar automatically
@@ -70,36 +58,6 @@ WORKFLOW:
7058
3. task_update({ id: "T-xxx", status: "completed" }) — when done
7159
7260
FORMAT: Keep subjects under 35 chars. One in_progress at a time.
73-
</SidebarTodos>`
74-
}
75-
return `<SidebarTodos>
76-
The user sees a Todo sidebar (~35 char width). Use todowrite strategically:
77-
78-
WHEN PLANNING (multi-step work):
79-
- Create "in_progress": "Planning: [brief desc]"
80-
- When plan ready: mark completed, add "Plan ready — /start-work"
81-
82-
WHEN DELEGATING TO AGENTS:
83-
- FIRST: Create "in_progress": "[agent]: [task]" (e.g. "thread: scan models")
84-
- The todowrite call MUST come BEFORE the Task/call_weave_agent tool call in your response
85-
- Mark "completed" AFTER summarizing what the agent returned
86-
- If multiple delegations: one todo per active agent
87-
88-
WHEN DOING QUICK TASKS (no plan needed):
89-
- One "in_progress" todo for current step
90-
- Mark "completed" immediately when done
91-
92-
FORMAT RULES:
93-
- Max 35 chars per todo content
94-
- Max 5 visible todos at any time
95-
- in_progress = yellow highlight — use for ACTIVE work only
96-
- Prefix delegations with agent name
97-
98-
BEFORE FINISHING (MANDATORY):
99-
- ALWAYS issue a final todowrite before your last response
100-
- Mark ALL in_progress items → "completed" (or "cancelled")
101-
- Never leave in_progress items when done
102-
- This is NON-NEGOTIABLE — skipping it breaks the UI
10361
</SidebarTodos>`
10462
}
10563

@@ -141,7 +99,7 @@ ${lines.join("\n")}
14199
</Delegation>`
142100
}
143101

144-
export function buildDelegationNarrationSection(disabled: Set<string> = new Set(), taskSystemEnabled = false): string {
102+
export function buildDelegationNarrationSection(disabled: Set<string> = new Set()): string {
145103
const hints: string[] = []
146104
if (isAgentEnabled("pattern", disabled)) {
147105
hints.push('- Pattern (planning): "This may take a moment — Pattern is researching the codebase and writing a detailed plan..."')
@@ -159,18 +117,6 @@ export function buildDelegationNarrationSection(disabled: Set<string> = new Set(
159117
? `\nDURATION HINTS — tell the user when something takes time:\n${hints.join("\n")}`
160118
: ""
161119

162-
const trackingTool = taskSystemEnabled ? "task_create/task_update" : "todowrite"
163-
const trackingInstr = taskSystemEnabled
164-
? `2. BEFORE the Task tool call: Create a task (in_progress) for the delegation using task_create.
165-
This ensures the sidebar updates immediately, not after the subagent finishes.`
166-
: `2. BEFORE the Task tool call: Create/update a sidebar todo (in_progress) for the delegation.
167-
The todowrite call MUST appear BEFORE the Task tool call in your response.
168-
This ensures the sidebar updates immediately, not after the subagent finishes.`
169-
170-
const completeInstr = taskSystemEnabled
171-
? `4. Mark the delegation task as "completed" using task_update after summarizing results.`
172-
: `4. Mark the delegation todo as "completed" after summarizing results.`
173-
174120
return `<DelegationNarration>
175121
EVERY delegation MUST follow this pattern — no exceptions:
176122
@@ -179,14 +125,15 @@ EVERY delegation MUST follow this pattern — no exceptions:
179125
- "Asking Pattern to create an implementation plan for the new feature..."
180126
- "Sending to Spindle to research the library's API docs..."
181127
182-
${trackingInstr}
128+
2. BEFORE the Task tool call: Create a task (in_progress) for the delegation using task_create.
129+
This ensures the sidebar updates immediately, not after the subagent finishes.
183130
184131
3. AFTER the agent returns: Write a brief summary of what was found/produced:
185132
- "Thread found 3 files related to auth: src/auth/login.ts, src/auth/session.ts, src/auth/middleware.ts"
186133
- "Pattern saved the plan to .weave/plans/feature-x.md with 7 tasks"
187134
- "Spindle confirmed the library supports streaming — docs at [url]"
188135
189-
${completeInstr}
136+
4. Mark the delegation task as "completed" using task_update after summarizing results.
190137
${hintsBlock}
191138
192139
The user should NEVER see a blank pause with no explanation. If you're about to call Task, WRITE SOMETHING FIRST.
@@ -250,6 +197,10 @@ export function buildPlanWorkflowSection(disabled: Set<string>): string {
250197
- SKIP plan workflow: Quick fixes, single-file changes, simple questions`)
251198

252199
return `<PlanWorkflow>
200+
CRITICAL: NEVER execute plan tasks directly. ALWAYS delegate to Tapestry via /start-work.
201+
Only Tapestry has the plan execution protocol (checkbox marking, verification, post-execution review).
202+
If you execute plan tasks yourself, checkboxes won't be marked and progress will be lost.
203+
253204
For complex tasks that benefit from structured planning before execution:
254205
255206
${steps.join("\n")}
@@ -351,15 +302,14 @@ export function composeLoomPrompt(options: LoomPromptOptions = {}): string {
351302
const disabled = options.disabledAgents ?? new Set()
352303
const fingerprint = options.fingerprint
353304
const customAgents = options.customAgents ?? []
354-
const taskSystemEnabled = options.taskSystemEnabled ?? false
355305

356306
const sections = [
357307
buildRoleSection(),
358308
buildProjectContextSection(fingerprint),
359-
buildDisciplineSection(taskSystemEnabled),
360-
buildSidebarTodosSection(taskSystemEnabled),
309+
buildDisciplineSection(),
310+
buildSidebarTodosSection(),
361311
buildDelegationSection(disabled),
362-
buildDelegationNarrationSection(disabled, taskSystemEnabled),
312+
buildDelegationNarrationSection(disabled),
363313
buildCustomAgentDelegationSection(customAgents, disabled),
364314
buildPlanWorkflowSection(disabled),
365315
buildReviewWorkflowSection(disabled),

src/agents/tapestry/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ export type { TapestryPromptOptions } from "./prompt-composer"
99
/**
1010
* Create a Tapestry agent config with optional disabled agents for prompt composition.
1111
*/
12-
export function createTapestryAgentWithOptions(model: string, disabledAgents?: Set<string>, taskSystemEnabled?: boolean): AgentConfig {
13-
if ((!disabledAgents || disabledAgents.size === 0) && !taskSystemEnabled) {
12+
export function createTapestryAgentWithOptions(model: string, disabledAgents?: Set<string>): AgentConfig {
13+
if (!disabledAgents || disabledAgents.size === 0) {
1414
return { ...TAPESTRY_DEFAULTS, tools: { ...TAPESTRY_DEFAULTS.tools }, model, mode: "primary" }
1515
}
1616
return {
1717
...TAPESTRY_DEFAULTS,
1818
tools: { ...TAPESTRY_DEFAULTS.tools },
19-
prompt: composeTapestryPrompt({ disabledAgents, taskSystemEnabled }),
19+
prompt: composeTapestryPrompt({ disabledAgents }),
2020
model,
2121
mode: "primary",
2222
}

0 commit comments

Comments
 (0)