Context
Leaf of #17559 (laws 0–2), owned by the author of the offending view. The Tasks surface (#17329 / PR #17542) ships a correct data path — the fleetTasks read-observe verb, the fleetTasksSource reducers with their producer-contract witnesses, FleetTask model + FleetTasks store — under a view layer that hand-rolls three sections of nested containers and per-row components. The operator critique of 2026-08-22 names it directly: a container with nested containers and components where a library primitive belongs.
The Problem
TasksPane renders running / queued / recent rows by mapping records into container configs (rowConfig), re-creating the row components on every snapshot. Twelve rows never needed buffering, but they did need a list: item identity, header sections, keyboard navigation and selection are all primitives Neo.list.Base already owns.
The Architectural Reality
Neo.list.Base: store-bound items (createItem/createItemContent), useHeaders with isHeader records (src/list/Base.mjs createItem's neo-list-header branch), native focus/arrow navigation, selectionModel.
- The existing row grammar (state word as the always-present text channel, native
<progress value max> + percent/done / total text, the source pill, the T5 viewer-time title) is a presentation contract to preserve — it becomes the createItemContent output.
- The envelope/freshness line (wired · partial · unavailable, the reason, the labeled cold sample) and the Refresh intent stay; they belong to a thin
tasks/Container that hosts the list, with a tasks/Controller owning the refresh intent (law 2). The cockpit's read drive (boot / liveness tick / reconnect) stays where it is until #17335's decomposition moves it.
The Fix
view/fleet/tasks/List.mjs — AgentOS.view.fleet.tasks.List extends Neo.list.Base, bound to the provider-hosted tasks store; useHeaders: true; the view inserts one header record per non-empty section at section boundaries (store sorted by section order, then the source's ordering); createItemContent renders the existing row grammar verbatim.
view/fleet/tasks/Container.mjs — the freshness line + Refresh + the list; tasks/Controller.mjs — the refresh intent (tasksRequest) and the cold/partial/wired presentation state.
- The cold-spine sample stays a labeled sample (sample records in the store under the unavailable envelope), never beside live rows — the existing contract, now expressed as store content.
TasksPane.scss → tasks/List.scss + tasks/Container.scss; tokens only; the freshness pill stays the shared cockpit vocabulary.
tasksPane.spec.mjs and FleetTasksPaneNL.spec.mjs migrate to the list (same assertions: progress value/max in a real DOM, refresh → second wire request → replacement).
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
AgentOS.view.fleet.tasks.List (new) |
#17559 laws 0+1; Neo.list.Base |
store-bound list with section header records and the existing row grammar |
empty section → no header; unavailable envelope → labeled sample records |
class docblock |
migrated unit + e2e specs |
tasksRequest intent |
existing cockpit controller seam |
fired by tasks/Controller, consumed by the cockpit unchanged |
n/a |
docblock |
existing cockpit spec |
fleetTasks verb / source / model / store |
#17329 ledger |
unchanged |
n/a |
existing |
existing 29 + 8 witnesses stay green |
Acceptance Criteria
Out of Scope
Moving the cockpit's read drive into the surface controller (#17335), buffering (twelve rows), any change to the fleetTasks wire contract.
Related
Epic: #17559. Blocked by: PR #17542 (the data path must land first) and the topology move leaf (born in the new folder). Origin of the view: #17329.
Live latest-open sweep: checked latest 20 open issues at 2026-08-22T16:55:35Z; no equivalent. A2A claim sweep: no competing claim.
Origin Session ID: 28bee2e0-4dc8-4375-8514-78fcf38d0d30
Retrieval Hint: query_raw_memories("tasks view list.Base section header records useHeaders row grammar progress")
Context
Leaf of #17559 (laws 0–2), owned by the author of the offending view. The Tasks surface (#17329 / PR #17542) ships a correct data path — the
fleetTasksread-observe verb, thefleetTasksSourcereducers with their producer-contract witnesses,FleetTaskmodel +FleetTasksstore — under a view layer that hand-rolls three sections of nested containers and per-row components. The operator critique of 2026-08-22 names it directly: a container with nested containers and components where a library primitive belongs.The Problem
TasksPanerendersrunning/queued/recentrows by mapping records into container configs (rowConfig), re-creating the row components on every snapshot. Twelve rows never needed buffering, but they did need a list: item identity, header sections, keyboard navigation and selection are all primitivesNeo.list.Basealready owns.The Architectural Reality
Neo.list.Base: store-bound items (createItem/createItemContent),useHeaderswithisHeaderrecords (src/list/Base.mjscreateItem'sneo-list-headerbranch), native focus/arrow navigation,selectionModel.<progress value max>+ percent/done / totaltext, the source pill, the T5 viewer-time title) is a presentation contract to preserve — it becomes thecreateItemContentoutput.tasks/Containerthat hosts the list, with atasks/Controllerowning the refresh intent (law 2). The cockpit's read drive (boot / liveness tick / reconnect) stays where it is until #17335's decomposition moves it.The Fix
view/fleet/tasks/List.mjs—AgentOS.view.fleet.tasks.List extends Neo.list.Base, bound to the provider-hosted tasks store;useHeaders: true; the view inserts one header record per non-empty section at section boundaries (store sorted by section order, then the source's ordering);createItemContentrenders the existing row grammar verbatim.view/fleet/tasks/Container.mjs— the freshness line + Refresh + the list;tasks/Controller.mjs— the refresh intent (tasksRequest) and the cold/partial/wired presentation state.TasksPane.scss→tasks/List.scss+tasks/Container.scss; tokens only; the freshness pill stays the shared cockpit vocabulary.tasksPane.spec.mjsandFleetTasksPaneNL.spec.mjsmigrate to the list (same assertions: progress value/max in a real DOM, refresh → second wire request → replacement).Contract Ledger Matrix
AgentOS.view.fleet.tasks.List(new)Neo.list.BasetasksRequestintenttasks/Controller, consumed by the cockpit unchangedfleetTasksverb / source / model / storeAcceptance Criteria
Neo.list.BasewithuseHeaderssection records — the same three sections, the same row grammar (state word +<progress value max>+ text + source pill + T5 title), witnessed in the migrated unit spec.FleetTasksPaneNLjourney).check-theme-surfacesgreen.Out of Scope
Moving the cockpit's read drive into the surface controller (#17335), buffering (twelve rows), any change to the
fleetTaskswire contract.Related
Epic: #17559. Blocked by: PR #17542 (the data path must land first) and the topology move leaf (born in the new folder). Origin of the view: #17329.
Live latest-open sweep: checked latest 20 open issues at 2026-08-22T16:55:35Z; no equivalent. A2A claim sweep: no competing claim.
Origin Session ID: 28bee2e0-4dc8-4375-8514-78fcf38d0d30 Retrieval Hint:
query_raw_memories("tasks view list.Base section header records useHeaders row grammar progress")