Merge pull request #22 from bnema/fix/taskede-phases-install
fix(tasked-phases): guide continuous checklist updates
4 files · +29 -4
M examples/extensions/tasked-phases/README.md +5 -3
| @@ -57,6 +57,8 @@ available, the plan is also stored as extension-owned state on each session | |||
| 57 | 57 | branch and restored when sessions are opened, switched, or forked. | |
| 58 | 58 | ||
| 59 | 59 | Before each model turn, the extension supplies only the active phase, its goal, | |
| 60 | - | and a bounded set of incomplete tasks as hidden context. Completed phases and | |
| 61 | - | future phases are not repeated in every provider request; use `get_status` or | |
| 62 | - | `/phases` when the full checklist is needed. | |
| 60 | + | and a bounded set of incomplete tasks as hidden context. The same context | |
| 61 | + | reminds the model to update the checklist continuously, mark each task as soon | |
| 62 | + | as it is completed, and advance the current phase when work moves forward. | |
| 63 | + | Completed phases and future phases are not repeated in every provider request; | |
| 64 | + | use `get_status` or `/phases` when the full checklist is needed. | |
| @@ -57,6 +57,8 @@ available, the plan is also stored as extension-owned state on each session | |||||
| 57 | branch and restored when sessions are opened, switched, or forked. | 57 | branch and restored when sessions are opened, switched, or forked. | ||
| 58 | 58 | ||||
| 59 | Before each model turn, the extension supplies only the active phase, its goal, | 59 | Before each model turn, the extension supplies only the active phase, its goal, | ||
| 60 | - | and a bounded set of incomplete tasks as hidden context. Completed phases and | 60 | + | and a bounded set of incomplete tasks as hidden context. The same context |
| 61 | - | future phases are not repeated in every provider request; use `get_status` or | 61 | + | reminds the model to update the checklist continuously, mark each task as soon |
| 62 | - | `/phases` when the full checklist is needed. | 62 | + | as it is completed, and advance the current phase when work moves forward. |
| 63 | + | Completed phases and future phases are not repeated in every provider request; | |||
| 64 | + | use `get_status` or `/phases` when the full checklist is needed. | |||
M examples/extensions/tasked-phases/main.go +1 -1
| @@ -118,7 +118,7 @@ func main() { | |||
| 118 | 118 | }) | |
| 119 | 119 | ||
| 120 | 120 | e.Tool(toolName, | |
| 121 | - | "Persist and update a structured spec, phased plan, current phase, and checklist tasks. Use it for spec-driven planning and progress tracking.", | |
| 121 | + | "Persist and update a structured spec, phased plan, current phase, and checklist tasks. Keep the plan current throughout implementation: update it continuously, call set_task_checked immediately after each completed checklist task, and call set_current_phase when moving to another phase.", | |
| 122 | 122 | json.RawMessage(toolSchema), a.handleTool) | |
| 123 | 123 | e.Command("phases", "Show the current spec, phases, and checklist state", a.handleCommand) | |
| 124 | 124 | e.OnPanelKey(panelID, a.handlePanelKey, a.handlePanelClose) | |
| @@ -118,7 +118,7 @@ func main() { | |||||
| 118 | }) | 118 | }) | ||
| 119 | 119 | ||||
| 120 | e.Tool(toolName, | 120 | e.Tool(toolName, | ||
| 121 | - | "Persist and update a structured spec, phased plan, current phase, and checklist tasks. Use it for spec-driven planning and progress tracking.", | 121 | + | "Persist and update a structured spec, phased plan, current phase, and checklist tasks. Keep the plan current throughout implementation: update it continuously, call set_task_checked immediately after each completed checklist task, and call set_current_phase when moving to another phase.", |
| 122 | json.RawMessage(toolSchema), a.handleTool) | 122 | json.RawMessage(toolSchema), a.handleTool) | ||
| 123 | e.Command("phases", "Show the current spec, phases, and checklist state", a.handleCommand) | 123 | e.Command("phases", "Show the current spec, phases, and checklist state", a.handleCommand) | ||
| 124 | e.OnPanelKey(panelID, a.handlePanelKey, a.handlePanelClose) | 124 | e.OnPanelKey(panelID, a.handlePanelKey, a.handlePanelClose) | ||
M examples/extensions/tasked-phases/state.go +7 -0
| @@ -915,7 +915,14 @@ func buildTurnContext(state PlanState) string { | |||
| 915 | 915 | return "" | |
| 916 | 916 | } | |
| 917 | 917 | lines := []string{ | |
| 918 | + | "[TASKED PHASES STATE - SOURCE OF TRUTH]", | |
| 918 | 919 | "Tasked phases focus:", | |
| 920 | + | "Call tasked_phases get_status if the exact full checklist is needed.", | |
| 921 | + | "Update tasked_phases continuously while implementing, not only at the end.", | |
| 922 | + | "After completing each checklist task, immediately call tasked_phases set_task_checked.", | |
| 923 | + | "After moving to another phase, immediately call tasked_phases set_current_phase.", | |
| 924 | + | "When a phase is shown as Title [id], pass phaseId as the raw id only, without brackets.", | |
| 925 | + | "Do not rely on prose alone for completion state.", | |
| 919 | 926 | fmt.Sprintf("Current phase: %s [%s]", formatPhaseTitle(*phase), formatTurnContextID(phase.ID)), | |
| 920 | 927 | } | |
| 921 | 928 | if phase.Goal != "" { | |
| @@ -915,7 +915,14 @@ func buildTurnContext(state PlanState) string { | |||||
| 915 | return "" | 915 | return "" | ||
| 916 | } | 916 | } | ||
| 917 | lines := []string{ | 917 | lines := []string{ | ||
| 918 | + | "[TASKED PHASES STATE - SOURCE OF TRUTH]", | |||
| 918 | "Tasked phases focus:", | 919 | "Tasked phases focus:", | ||
| 920 | + | "Call tasked_phases get_status if the exact full checklist is needed.", | |||
| 921 | + | "Update tasked_phases continuously while implementing, not only at the end.", | |||
| 922 | + | "After completing each checklist task, immediately call tasked_phases set_task_checked.", | |||
| 923 | + | "After moving to another phase, immediately call tasked_phases set_current_phase.", | |||
| 924 | + | "When a phase is shown as Title [id], pass phaseId as the raw id only, without brackets.", | |||
| 925 | + | "Do not rely on prose alone for completion state.", | |||
| 919 | fmt.Sprintf("Current phase: %s [%s]", formatPhaseTitle(*phase), formatTurnContextID(phase.ID)), | 926 | fmt.Sprintf("Current phase: %s [%s]", formatPhaseTitle(*phase), formatTurnContextID(phase.ID)), | ||
| 920 | } | 927 | } | ||
| 921 | if phase.Goal != "" { | 928 | if phase.Goal != "" { | ||
M examples/extensions/tasked-phases/state_test.go +16 -0
| @@ -238,6 +238,22 @@ func TestTurnContextContainsOnlyCurrentFocus(t *testing.T) { | |||
| 238 | 238 | } | |
| 239 | 239 | } | |
| 240 | 240 | ||
| 241 | + | func TestTurnContextRemindsAgentToTrackProgressContinuously(t *testing.T) { | |
| 242 | + | context := buildTurnContext(sampleState()) | |
| 243 | + | for _, want := range []string{ | |
| 244 | + | "[TASKED PHASES STATE - SOURCE OF TRUTH]", | |
| 245 | + | "Update tasked_phases continuously while implementing, not only at the end.", | |
| 246 | + | "After completing each checklist task, immediately call tasked_phases set_task_checked.", | |
| 247 | + | "After moving to another phase, immediately call tasked_phases set_current_phase.", | |
| 248 | + | "When a phase is shown as Title [id], pass phaseId as the raw id only, without brackets.", | |
| 249 | + | "Do not rely on prose alone for completion state.", | |
| 250 | + | } { | |
| 251 | + | if !strings.Contains(context, want) { | |
| 252 | + | t.Errorf("turn context missing continuous-tracking guidance %q: %s", want, context) | |
| 253 | + | } | |
| 254 | + | } | |
| 255 | + | } | |
| 256 | + | ||
| 241 | 257 | func TestTurnContextIsBoundedForUntrustedIdentifiers(t *testing.T) { | |
| 242 | 258 | state := sampleState() | |
| 243 | 259 | state.Phases[1].ID = strings.Repeat("phase-id-", 1000) | |
| @@ -238,6 +238,22 @@ func TestTurnContextContainsOnlyCurrentFocus(t *testing.T) { | |||||
| 238 | } | 238 | } | ||
| 239 | } | 239 | } | ||
| 240 | 240 | ||||
| 241 | + | func TestTurnContextRemindsAgentToTrackProgressContinuously(t *testing.T) { | |||
| 242 | + | context := buildTurnContext(sampleState()) | |||
| 243 | + | for _, want := range []string{ | |||
| 244 | + | "[TASKED PHASES STATE - SOURCE OF TRUTH]", | |||
| 245 | + | "Update tasked_phases continuously while implementing, not only at the end.", | |||
| 246 | + | "After completing each checklist task, immediately call tasked_phases set_task_checked.", | |||
| 247 | + | "After moving to another phase, immediately call tasked_phases set_current_phase.", | |||
| 248 | + | "When a phase is shown as Title [id], pass phaseId as the raw id only, without brackets.", | |||
| 249 | + | "Do not rely on prose alone for completion state.", | |||
| 250 | + | } { | |||
| 251 | + | if !strings.Contains(context, want) { | |||
| 252 | + | t.Errorf("turn context missing continuous-tracking guidance %q: %s", want, context) | |||
| 253 | + | } | |||
| 254 | + | } | |||
| 255 | + | } | |||
| 256 | + | ||||
| 241 | func TestTurnContextIsBoundedForUntrustedIdentifiers(t *testing.T) { | 257 | func TestTurnContextIsBoundedForUntrustedIdentifiers(t *testing.T) { | ||
| 242 | state := sampleState() | 258 | state := sampleState() | ||
| 243 | state.Phases[1].ID = strings.Repeat("phase-id-", 1000) | 259 | state.Phases[1].ID = strings.Repeat("phase-id-", 1000) | ||