SIGN IN SIGN UP

feat(ui): add failed todo state and reason (#6867)

## Problem

`elements-todo-list` shipped three statuses and no way to record that a task ended without completing. an agent that hits a failed tool or an ask it cannot serve has to either leave the item `active`, which renders a spinner indefinitely, or mark it `done`, which is false. the progress counter has the same gap: a list where everything has settled but one item failed reads `2/3` forever, and every consumer adding a failure state would decide independently whether it counts.

Closes #6468

## Change

adds `"failed"` to `TodoStatus` and an optional `reason?: string` on `TodoItem`, rendered under the item text when the status is `failed`.

the counter keeps `done` as its numerator. a failed item is terminal but it is not a success, and an all-failed list reading `3/3` would be indistinguishable from three successes, so failed items count toward the denominator and never toward it. the docs now state that rule instead of leaving each consumer to guess.

`TodoItem` stays a plain interface. an earlier revision modelled `reason` with a `reason?: never` discriminated union, which rejects the exact shape the documented zod schema produces, so neither the `render` example nor the `advance` snippet on the same docs page compiled against it.

every row carries its status as `sr-only` text beside an `aria-hidden` icon, following `mcp-server-panel.tsx` and `agent-status.tsx`. before this only `failed` had an accessible name, and it got one through `role="status"`, which makes each failed row a polite live region inside a component that is rewritten on every tool call.

## Verification

- `vitest run src/components/react/assistant-ui/tests/elements.test.tsx` in `packages/ui`: 557 passed, 16 skipped
- `tsc --noEmit -p packages/ui/tsconfig.json`: no errors in `todo-list.tsx`; the remaining ones are pre-existing in `image.test.tsx`, `thread-list.aui.test.tsx`, and the vue tests
- both documented paths typecheck against the shipped type: the zod-derived `{ status: TodoStatus; reason?: string }[]` handed to `<TodoList items={...} />`, and the `advance()` map snippet
- `oxlint` and `oxfmt --check` clean on the changed files

## Public surface

- `@assistant-ui/ui` (private, so no changeset): `TodoStatus` gains `"failed"`, `TodoItem` gains `reason?: string`
- docs: `apps/docs/content/elements/todo-list.mdx` (schema, prose, anatomy, examples, props table)
S
Solaris-star committed
c644837c8fe12da4bb4ca1a7e85c731405d44c98
Parent: 11678cb
Committed by GitHub <noreply@github.com> on 9/7/2026, 3:42:39 AM