NAVIGATION
ELOWEN DOCUMENTATION

Last updated: 10 October 2026

Browse documentation · todo implementation
Developer reference

todo implementation

Browser display contract

Todo requires browser UI API 32. The host's ElowenUiRuntime.utils.cardTasks projects the pushed Todo card once, preserving owner, unresolved blockers, running labels and measured times. The card uses that helper and the host's cardTasksAddressable guard; the rail passes the host's already-projected TaskRailData.tasks directly to TaskList. Neither surface reinterprets glued text or polls the task route. The host normalizes incoming plugin cards at the emit boundary; the rail payload is internal host data. Missing cards yield no rows and empty or partially unaddressable lists have no interactive controls. The projection is linear in the supplied cards and Todo rows and adds no network request. Missing or incompatible bundles retain the host's fallback. The Tasks management picker still reads its authorized route on demand.

The unreleased browser bundle requires the host's hooks.useNow member without changing its API number. TodoCard calls hooks.useNow(1000, enabled) only for a live card with a running timestamp; the rail subscribes while one of its rows has a running timestamp. One host heartbeat serves both surfaces, pauses notifications while the tab is hidden, catches up on visibility and stops after the last subscriber leaves. The rail evaluates the current time on each render, including the render requested by a status action. No plugin timer or network request is added. A host missing the hook fails explicitly instead of starting a local clock. Registry UI tests install the candidate's actual hook.

The pinned UI kit predates the host's API 32 declarations. Until that pin is updated, web-src/runtime.ts derives row fields from the kit's structured card item type and adds measured duration, rather than defining a second display projection. UI tests use a test-only host projection port, never plugin logic.

Storage contract

The internal lib/taskStore.mjs module owns the SQLite migrations, task rows, dependency graph, metadata and completed-list aging. lib/tasks.mjs creates one TaskStore during registration and uses it for the HTTP routes, task tools and context providers. Construction applies the existing plugin migrations before preparing queries; no store or context provider is created while Todo is disabled. Account and conversation authorization stays at the registration boundary, which supplies the list key to each store operation.

Batch creation, updates, deletion and clearing retain their transaction boundaries. Graph traversal stays inside the store, and the HTTP and tool entry points share its control-character rule. List reads load only that conversation's tasks and edges; aging runs only when a fresh turn is composed. An unreadable metadata column is logged once per store instance and left byte-for-byte intact by unrelated updates; an explicit metadata patch replaces it. This split adds no query, timer or network request.

Rendering and update contracts

Import xmlEscape from elowen-plugin-shared/xml and call xmlEscape(value) when rendering XML text or attributes. It replaces XML 1.0-forbidden C0 characters, escapes markup and both quote kinds, and preserves Unicode, tab, LF, CR and DEL. It acts only at the XML rendering boundary and does not change stored values or network behavior. It is not single-line label validation: Todo still rejects controls in subjects and owners. Todo's turn context and Twilio's Stream URL attribute are real consumers. The runtime and declarations must be present in the containing core artifact.

Todo task diagnostics use errorText from elowen-plugin-shared/errors. errorText(error) retains Error.message and otherwise uses String(error); conversion failures propagate. It formats only an existing failure and does not log, retry or invent a success result. TaskStore owns UPDATABLE_FIELDS for both update admission and TaskUpdate's corrective hint: supplied scalar fields count as an update, while dependency arrays must be non-empty. Unknown tool keys remain ignored and a patch without a supported change is refused before persistence.

Todo's task picker mounts the shared Modal without the retired intent prop; the host owns overlay presentation and focus. In the turn context, at most 10 finished tasks are listed one by one; older finished tasks fold into one counted line, and their rows and IDs remain until the completed-list grace period ends.

Next: Voice Calls