LearnNewsExamplesServices
Frontmatter
id17559
titleCockpit view layer conforms to the component library
stateOpen
labels
epicdesignaiarchitectureagent-os
assignees[]
createdAtAug 22, 2026, 6:56 PM
updatedAtAug 23, 2026, 3:01 AM
githubUrlhttps://github.com/neomjs/neo/issues/17559
authorneo-fable-clio
commentsCount1
parentIssue14560
subIssues
17553 Fleet grid: animated sortable roster; selection drives the panes
17335 Decompose FleetCockpit.mjs below the 1k-LOC app-file bar
17550 Activity stream: scrollable buffered list, honest counts, per-row local times
17268 FM pane information design: mailbox, memories and catch-up as designed views
17549 Cockpit header and agent detail: information architecture and responsiveness
17560 Fleet view folder: topology move and base-class naming (mechanical)
17561 Tasks view: a list.Base with section header records
17568 Fleet util modules become classes with owned placement
17601 The AgentCard contract still declares a retired Button the card''s only disclosure route
subIssuesCompleted6
subIssuesTotal9
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Cockpit view layer conforms to the component library

Open Backlog/active-chunk-18 epicdesignaiarchitectureagent-os
neo-fable-clio
neo-fable-clio commented on Aug 22, 2026, 6:56 PM

Problem scope

Operator architecture critique, paired session 2026-08-22, on AgentOS.view.fleet.TasksPane and its siblings: "Pane violates neo naming conventions — Pane is not a base class. You chose a container with nested containers and components, not a buffered grid or list. This is a problem in other views too — ignoring what the neo component library offers. BUFFERED views matter: activity, memories, a2a messages, maybe more. Dropping everything into ONE fleet folder — camelCase and PascalCase wild mixture. Missing PLANNING."

The inventory (2026-08-22, apps/agentos/view/fleet/, 43 files: 27 PascalCase classes + 16 camelCase modules in one flat folder):

  • The suffix lies about the base class. Twelve classes extend Neo.container.Base and none is named …ContainerActivityStream, AgentCard, AgentDetail, CatchUpPane, FleetCockpit, FleetGrid (a container, not a grid), HealthBar, InstanceManager, MailboxPane, MemoriesPane, OperatorMailbox, WakeRoutePane. Seven extend Neo.component.Base without …Component (ActorChip, AgentConfigCard, EventChip, FamilyRail, HealthSwatch, SourceHealthMarker, StateDot). The only correctly-suffixed classes are the ones that consume library primitives (RecipientChipList, InstanceMenuList, InstanceSwitcher → Button, the two …Form containers).
  • Data-carrying views hand-roll their rows. Activity events, agent and operator mailbox messages, session summaries/turns, catch-up entries, wake-route seats, the roster and the tasks rows are all rendered as nested containers/components from hand-mapped records — while Neo.grid.Container (buffered via bufferRowRange, sorting, filtering), Neo.list.Base / Neo.list.Component (component items, list.plugin.Animate) and their extension points sit unused. The app-work gate's store-binding rule was applied to the data layer and ignored at the view layer: a Store exists, a primitive does not.
  • State and logic live in the wrong layer. FleetCockpit.mjs carries 4,018 lines of imperative plumbing — getMemoriesPane()?.set({agentOptions…})-style pushes, per-pane loaders, selection held as owner fields — where the engine offers hierarchical state.Providers (bindings) and per-view controller.Components. apps/portal/view/** is the in-repo precedent: a folder per surface, each with MainContainer + MainContainerController + MainContainerStateProvider, ten controllers, components named by base class.
  • The mechanism that produced it: every leaf "read 1–2 siblings" and conformed to the sibling rather than to the library — precedent-following, the named anti-pattern in ticket-create §8 — and no view-architecture plan existed to conform to. The cockpit grew one ticket at a time into a flat folder.

Why an Epic: the repair spans a mechanical topology move (touching every file, spec, dock item id and SCSS class), per-view primitive migrations, a controller/provider decomposition, and a substrate line in the app-work gate — coordinated toward one outcome, deliverable only as several one-PR leaves in a fixed order.

Intended solution shape — the four laws (operator-stated, 2026-08-22)

  1. Base class first, library second, custom last. Before designing a view: which library class already does this? A headerless "table" in the design spec is a custom-styled Neo.grid.Container (sorting, filtering, buffered rendering for free); an item collection is a Neo.list.Base or a Neo.list.Component with component-based items; both extend cleanly. Composition inside views stays welcome — hand-rolled data views do not.
  2. The suffix IS the base family: …Container, …Component, …List, …Grid (only for a real grid.Container), …Controller, …StateProvider, …Form, …Button. Role words ("Pane", "Stream", "Card", "Detail", "Bar", "Rail", "Dot") become folder names, never suffixes; the folder supplies the namespace (AgentOS.view.fleet.tasks.Container, …roster.List, …mailbox.Grid).
  3. Controllers own business logic; providers own cross-view state. Each surface gets a …Controller (intents, lifecycle round-trips, reads) and, where state is shared, a nested …StateProvider under the cockpit-root provider — e.g. the selected agent becomes provider data that the detail and memories views consume through bindings, replacing the imperative set() pushes. This is also the decomposition method for the cockpit root itself.
  4. Topology mirrors the surfaces: view/fleet/ becomes cockpit/, roster/ (+ roster/card/), activity/, tasks/, memories/, mailbox/ (agent + operator + compose + recipient chips), detail/, instances/, health/. The camelCase pure-function modules (Brain-side style that leaked into Body-side app code) become Body-style util classes under apps/agentos/util/ (AgentOS.util.ViewerTime, …SourceHealth, …).

Primitive map (the direction, refined per leaf): mailbox surfaces, memories and catch-up → grid.Container; the roster → list.Component + list.plugin.Animate; tasks (≤12 rows, three sections) → list.Base with useHeaders section records; activity → grid.Container or the buffered component list (#17554), chosen by whether its rows need component items; wake routes → a list.

Order, no big-bang: the in-flight fleet-folder PRs land first; then ONE mechanical move-and-rename leaf relocates the EXISTING tree; per-view primitive migrations follow as their own leaves; the cockpit-root decomposition rides on the controllers/providers law. New-surface leaves never wait for the move leaf: a new surface is born conformant in its own folder with law-1 names (additive folders cannot conflict with the mechanical move, whose file list is whatever exists at its rebase time); a new view that replaces an old file retires that file in its own PR.

Out of scope

  • Visual design of the panes (the §04 bar, #17268-class content design) — this epic governs class shape, placement and primitives, not typography.
  • The dock workspace host migration (#17539 arc) — the cockpit leaf of that arc is planned inside this topology, not replaced by it.
  • New engine primitives beyond what leaves need (the buffered component list has its own engine ticket).

Avoided traps

  • Big-bang rewrite: one PR touching 43 files + every spec is unreviewable; the move leaf is mechanical-only, the primitive migrations are per view.
  • Renaming without re-basing: a MailboxPane renamed to MailboxContainer that still hand-rolls rows has conformed to law 1 and violated law 0; each migration leaf picks the primitive first.
  • Controllers as a second component: business logic moves to controllers; views keep composition and rendering, providers keep state — no fourth layer.

Related

Parent: #14560. Precedent: apps/portal/view/**. Structure map: ai:structure-map --root apps/agentos/view (view/ 3 files, view/fleet 43 files — the flat folder this epic dissolves).

Decision Record impact: none — applies existing engine conventions (src/ naming, Portal view topology); no ADR amended.

Live latest-open sweep: checked latest 20 open issues at 2026-08-22T16:55:35Z; no equivalent epic (adjacent leaves #17335, #17553, #17550, #17554 become/are linked subs). A2A claim sweep: no competing claim.

Origin Session ID: 28bee2e0-4dc8-4375-8514-78fcf38d0d30 Retrieval Hint: query_raw_memories("cockpit view layer conforms component library suffix base class topology controllers providers buffered grid")