docs/superpowers/plans/2026-04-27-background-task-retry-timeline.md
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Add structured retry-attempt history to background tasks and surface a compact attempt timeline in parent chat while preserving separate retry child sessions.
Architecture: Extend BackgroundTask with explicit attempts[] state and currentAttemptID, add small helper functions to keep task-level fields as a projection of the current attempt, and wire those helpers into background retry, session creation, and completion/error paths. Parent notifications remain the UI surface, but they are generated from structured attempt state instead of ad hoc retry text.
Tech Stack: TypeScript, Bun test, OpenCode background task engine, parent chat notification flow
src/features/background-agent/types.ts
BackgroundTask with attempts[] and currentAttemptIDsrc/features/background-agent/manager.ts
startTask()sessionID -> attemptIDattempts[]src/features/background-agent/fallback-retry-handler.ts
src/features/background-agent/background-task-notification-template.ts
src/tools/background-task/task-result-format.ts
src/features/background-agent/manager.test.tssrc/features/background-agent/fallback-retry-handler.test.tssrc/tools/background-task/task-result-format.test.tssrc/features/background-agent/session-idle-event-handler.tssrc/features/background-agent/task-history.tssrc/features/background-agent/session-status-classifier.tsdocs/superpowers/specs/2026-04-27-background-task-retry-timeline-design.mdFiles:
Modify: src/features/background-agent/types.ts
Test: src/features/background-agent/manager.test.ts
Step 1: Add a focused failing test that expects attempt state on a new background task
Add a test in src/features/background-agent/manager.test.ts that launches a background task and expects:
attempts to exist
first attempt to have attemptNumber: 1
currentAttemptID to point at that first attempt
top-level task fields to still exist for compatibility
Step 2: Run the new test to verify it fails for the expected reason
Run:
bun test src/features/background-agent/manager.test.ts
Expected: the new assertion fails because attempts[] and currentAttemptID do not exist yet.
BackgroundTaskUpdate src/features/background-agent/types.ts to add:
BackgroundTaskAttempt type/interface with:
attemptIDattemptNumbersessionID?providerID?modelID?variant?statuserror?startedAt?completedAt?attempts?: BackgroundTaskAttempt[]
currentAttemptID?: string
Step 4: Initialize first attempt state when tasks are created
In src/features/background-agent/manager.ts, when launch() creates the initial BackgroundTask, initialize:
one attempt entry in pending
currentAttemptID referencing that entry
top-level model copied into attempt model fields
Step 5: Re-run the test to verify the new task has attempt state
Run:
bun test src/features/background-agent/manager.test.ts
Expected: the new launch/creation test passes.
Files:
Modify: src/features/background-agent/manager.ts
Test: src/features/background-agent/manager.test.ts
Step 1: Add a failing test for exact attempt binding in startTask()
Add a test that simulates:
startTask() creating a child sessionThe test should assert:
sessionID lands on the correct attempt
currentAttemptID remains correct
top-level task sessionID mirrors that active attempt
Step 2: Run the test to verify it fails before helpers exist
Run:
bun test src/features/background-agent/manager.test.ts
Expected: binding assertions fail or require manual task mutation not yet implemented.
manager.tsAdd small focused helpers, either in manager.ts or a dedicated sibling helper file if needed:
startAttempt(task, initialModel)bindAttemptSession(task, attemptID, sessionID, model)scheduleRetryAttempt(task, failedAttemptID, nextModel, error)finalizeAttempt(task, attemptID, terminalStatus, error?)These helpers must enforce:
only currentAttemptID is mutable
finalized attempts are immutable
binding by explicit attemptID
Step 4: Add a sessionID -> attemptID mapping strategy
Implement one of:
The first implementation can be simple, but every lifecycle event must resolve the attempt through this mapping before mutating state.
attemptID into startTask()Update the implementation plan so queued background work carries the scheduled attemptID explicitly.
Concretely:
attemptIDattemptID at queue timestartTask() receives the exact attemptID and never infers “latest pending attempt”This is required to satisfy the approved spec’s exact-binding rule.
Run:
bun test src/features/background-agent/manager.test.ts
Expected: new binding and helper tests pass.
Files:
Modify: src/features/background-agent/fallback-retry-handler.ts
Test: src/features/background-agent/fallback-retry-handler.test.ts
Step 1: Add a failing test for retry scheduling creating Attempt 2
Add a test that starts with a task already representing Attempt 1 and then runs tryFallbackRetry().
Expected behavior:
Attempt 1 becomes terminal error
Attempt 2 is created as pending
currentAttemptID moves to Attempt 2
top-level task.model mirrors Attempt 2 model
Step 2: Run the retry-handler test to verify it fails
Run:
bun test src/features/background-agent/fallback-retry-handler.test.ts
Expected: no structured attempt chain exists yet, so assertions fail.
In src/features/background-agent/fallback-retry-handler.ts:
finalize the current attempt before retry queueing
create the next pending attempt
preserve retry notification metadata on the task
keep top-level compatibility fields aligned with the new active attempt
Step 4: Re-run the retry-handler tests
Run:
bun test src/features/background-agent/fallback-retry-handler.test.ts
Expected: retry now produces a correct attempt chain.
Files:
Modify: src/features/background-agent/manager.ts
Reference: src/features/background-agent/session-idle-event-handler.ts
Test: src/features/background-agent/manager.test.ts
Step 1: Add a failing stale-event regression test
Create a test that simulates:
sessionID arrivesExpected:
Attempt 2 and top-level task projection do not change
stale event is ignored for state mutation
Step 2: Run the test to verify the stale-event case fails first
Run:
bun test src/features/background-agent/manager.test.ts
Expected: stale-event mutation is not yet blocked.
sessionID -> attemptID firstApply this rule in relevant background manager paths:
message.updatedsession.errorsession.statusBefore mutating state:
attemptID from the incoming sessionIDcurrentAttemptIDRun:
bun test src/features/background-agent/manager.test.ts
Expected: stale-event regression passes.
Files:
Modify: src/features/background-agent/background-task-notification-template.ts
Test: src/features/background-agent/manager.test.ts
Step 1: Add a failing notification-format test for multi-attempt tasks
Create a test that builds a completed/failed task with three attempts and expects parent-facing summary text containing:
attempt number
status
model
session id
Step 2: Run the test to verify it fails first
Run:
bun test src/features/background-agent/manager.test.ts
Expected: current notifications do not include a structured attempt timeline.
In background-task-notification-template.ts:
keep the summary compact
render one line per attempt
include error text only for failed attempts where useful
do not replace separate retry reminders; final summary is additive
Step 4: Update manager-side aggregation so final summaries carry attempt history
notifyParentSession() currently batches through completedTaskSummaries in manager.ts, which only stores task-level summary data.
Modify that aggregation path so the final per-task notification has access to the task’s structured attempts[] data at summary time.
Allowed implementation directions:
BackgroundTaskNotificationTask to include attempt timeline dataThe key requirement is that the final parent summary must render the authoritative attempt timeline from structured state, not from task-level status alone.
Run:
bun test src/features/background-agent/manager.test.ts
Expected: parent-summary timeline is now shown from attempts[] state.
Files:
Modify: src/features/background-agent/manager.ts
Test: src/features/background-agent/manager.test.ts
Step 1: Add a failing test that retry-scheduled and retry-session-ready notifications are derived from attempt state
The test should verify:
retry scheduled reminder still includes failed session id, failed model, error, next model
retry session ready reminder includes new retry session id and attempt number
Step 2: Run the test to verify current behavior is incomplete
Run:
bun test src/features/background-agent/manager.test.ts
Expected: notifications are not yet driven by structured attempt state.
Make the existing retry observability path use attempts[] + currentAttemptID instead of ad hoc fields where practical.
Run:
bun test src/features/background-agent/manager.test.ts
Expected: retry observability remains correct after the attempt-state refactor.
Files:
Test: src/features/background-agent/manager.test.ts
Test: src/features/background-agent/fallback-retry-handler.test.ts
Test: src/tools/background-task/task-result-format.test.ts
Step 1: Add an end-to-end regression covering multiple retries followed by success
Test expectations:
3 attempts recorded
first two failed with distinct models/session ids
third completed successfully
parent summary contains all three attempts in order
Step 2: Run the focused regression suite
Run:
bun test src/features/background-agent/manager.test.ts src/features/background-agent/fallback-retry-handler.test.ts src/tools/background-task/task-result-format.test.ts
Expected: all focused tests pass.
Run:
bun test src/features/background-agent/manager.test.ts src/features/background-agent/fallback-retry-handler.test.ts src/features/background-agent/error-classifier.test.ts src/tools/background-task/task-result-format.test.ts src/tools/delegate-task/sync-session-poller.test.ts src/tools/delegate-task/sync-task.test.ts src/plugin/event.test.ts src/shared/model-error-classifier.test.ts
Expected: all tests pass.
Run:
bun run typecheck
bun run build
Expected: both commands succeed with no errors.
Files:
Review: all modified files above
Step 1: Manually verify task-level projection consistency
Check in code review that:
active attempt and top-level fields always agree
finalized attempts are not mutated later
stale events are ignored
Step 2: Confirm parent chat UX remains compact
Check that final attempt timeline is readable and not overly verbose.
Document:
files changed
new attempt-state invariants
tests added/updated
Step 4: Commit
git add src/features/background-agent/types.ts src/features/background-agent/manager.ts src/features/background-agent/fallback-retry-handler.ts src/features/background-agent/background-task-notification-template.ts src/features/background-agent/manager.test.ts src/features/background-agent/fallback-retry-handler.test.ts src/tools/background-task/task-result-format.test.ts docs/superpowers/specs/2026-04-27-background-task-retry-timeline-design.md docs/superpowers/plans/2026-04-27-background-task-retry-timeline.md
git commit -m "feat(background-task): add retry attempt timeline"
Plan complete and saved to docs/superpowers/plans/2026-04-27-background-task-retry-timeline.md. Ready to execute?