2.8 KiB
2.8 KiB
id, title, status, wave, depends_on, plan, spec
| id | title | status | wave | depends_on | plan | spec | |
|---|---|---|---|---|---|---|---|
| 02-persist-recovery-link | Persist recovery links | done | 2 |
|
plan.md | ../../specs/agents/requirements/agent-stall-recovery.md |
Task 02: Persist Recovery Links
Acceptance
LastAgentErrorpersists a validatedremediation_urlwithout changing the plainTaskSession.error_messagecontract.- Generic recoverable errors and quota-specialized errors both carry the link when the normalized provider diagnostic has one.
- The existing lifecycle generation guards still select the winning provider error; stale or invalid diagnostics cannot overwrite the persisted link.
session.state_changedincludes the structured metadata so connected Office clients receive the same value as a reload.
Verification
cd apps/backend && go test ./internal/task/models ./internal/orchestrator ./internal/agent/runtime/lifecycle -run 'Test(LastAgentError|HandleRecoverableFailure|CreateRecoveryStatusMessage|.*ProviderError)' -count=1
Assert the URL is present only in the dedicated field, while message,
error_output, error_message, and generic logs remain URL-free.
Files likely touched
apps/backend/internal/task/models/models.goapps/backend/internal/orchestrator/event_handlers_agent.goapps/backend/internal/orchestrator/event_handlers_test.goapps/backend/internal/orchestrator/recovery_actions_test.go- relevant lifecycle/session-state event tests
Dependencies and risks
Task 01. Reuse the existing ProviderError propagation; do not create a second
diagnostic event or a parallel session state. Preserve local dismissal and
last-error stamping behavior.
Results
Implemented 2026-08-07.
models.LastAgentError.RemediationURLpersisted underSessionMetaKeyLastAgentError; plainTaskSession.error_messagecontract unchanged and URL-free.providerRemediationURLcopies only the adapter-validated field from the normalizedProviderError;createRecoveryStatusMessagecarriesremediation_urlin the action-message metadata independently of quota classification (ACP-sourced diagnostics are not quota-classified but still show the link). Thetask_session.error_changedevent carries it, andsession.state_changedincludes it via the persistedsession_metadata.- Tests: quota + generic cards carry the URL; nil/invalid diagnostics produce
no metadata key;
handleRecoverableFailurepersists the URL and keepserror_messageURL-free; absent diagnostics omit the field.
Verification: go test ./internal/task/models ./internal/orchestrator ./internal/agent/runtime/lifecycle -run 'Test(LastAgentError|HandleRecoverableFailure|CreateRecoveryStatusMessage|.*ProviderError)' -count=1 — passed; full orchestrator (3094 tests) and lifecycle (1962 tests) suites passed.