LearnNewsExamplesServices
Frontmatter
id17303
titleActivity stream rows carry actor identity and A2A sender→recipient
stateClosed
labels
enhancementdesignaiagent-os
assigneesneo-fable-clio
createdAtAug 17, 2026, 6:21 PM
updatedAtAug 18, 2026, 5:41 PM
githubUrlhttps://github.com/neomjs/neo/issues/17303
authorneo-fable-clio
commentsCount0
parentIssue14560
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 18, 2026, 5:41 PM
milestonev13.2

Activity stream rows carry actor identity and A2A sender→recipient

Closed Backlog/active-chunk-17 enhancementdesignaiagent-os
neo-fable-clio
neo-fable-clio commented on Aug 17, 2026, 6:21 PM

Context

Operator feedback, live during the #17271 L1 paired session (2026-08-17): "lane claim: maybe an actor badge (who claimed something?), a2a badge => maybe sender and receiver badges, or one badge for both." Current rows are inconsistent: PR rows render an actor line ("neo-opus-grace · pr-activity"), while LANE/A2A rows render subject text only — WHO claimed or sent is invisible at a glance.

The Problem (the data is present; the render drops it)

ai/services/fleet/fleetA2AActivityAdapter.mjs (~L161-170) already maps agentId: message.from, plus from and recipientClass into the cockpit DTO — its own doc reads "the operator surface gets sender, recipient class". apps/agentos/view/fleet/ActivityStream.mjs coalesces rows BY agentId (same-actor runs), so actor identity is load-bearing in the data model — and then never rendered. The operator cannot see who did what without opening the underlying artifact.

The Architectural Reality

  • Row anatomy today: time + kind chip (EventChip owns kind rendering entirely) + subject text.
  • The coalescing contract (ActivityStream.mjs run rows: {agentId, count, events, newest}) already treats the actor as the primary key.
  • The roster join supplies avatarUrl per agentId (FleetAgent rows) — avatar-first badges are cheap.
  • #15536 (AgentCard responsive anatomy) established the avatar-invariant precedent: the avatar is the fast-recognition anchor and must survive density modes.
  • #17264 (unify the cockpit status-chip family) is the governing chip-system ticket — this row badge must be a SPECIMEN of that family, not a bespoke one-off.

The Fix

  1. ONE ActorChip per row: avatar-first (roster join), handle-text fallback; anonymous/unknown actor renders honest absence (no fabricated identity), per the stream's existing null-agentId contract.
  2. A2A rows additionally render the recipient half as a compact pair: @from → @to for directed sends, a distinct broadcast glyph for AGENT:* (recipientClass is already in the DTO — broadcast vs direct is an honest, cheap distinction).
  3. Coalesced runs render the actor once beside the count — the coalescing key becomes visible instead of implied.
  4. Chip mechanics live under the #17264 family (tokens, density ladder). If #17264 lands first, consume it; if this lands first, build the chip to its spec draft and note the reconciliation.

Acceptance Criteria

  • Every row kind (A2A / LANE / PR / REVIEW-ACTIVITY / …) renders an actor badge from the DTO's actor field; absence renders as absence.
  • A2A rows render sender→recipient; broadcast (AGENT:*) visually distinct from directed sends.
  • Coalesced run rows render the actor once with the run count.
  • Chip styling token-governed under the #17264 family (zero bespoke CSS values).
  • A2A DTO: recipientClass pre-existed, the directed recipient id did NOT — the adapter adds to, deliberately relaxing its documented class-only bound. Privacy holds because the caller owns the read: createA2AMessageActivityEvents has no production caller outside the module — the sole path is readFleetA2AActivitySnapshot, which maps caller-supplied listMessages() output and never fetches, so the DTO cannot disclose a message the caller had not already read. Any PR/lane actor-field gap closed adapter-side with the same honest-absence contract. (Edited 2026-08-18: the original AC read "DTO untouched for A2A (fields exist)" — falsified by the shipped diff, which the AC exists to pin; corrected per PR #17351 review-4962256855 RA-2.)
  • Density bound holds: the badge adds no second text line to the 12-row window (measured against the #15037 freeze).

Out of Scope

Pane-level mailbox rendering (#17268) · new event kinds · actor filtering/grouping UI · timestamp formatting (sibling ticket).

Related

Epic #14560 (parent) · #17264 (chip family — governing system) · #15037 (density freeze) · #15536 (avatar-invariant precedent) · #17268 (panes) · #17271 (origin session)

Live latest-open sweep: latest 20 open checked 2026-08-17T16:19Z, no equivalent; A2A claim window (last 60 min) clean; KB semantic sweep clean (hits were parent/adjacent: #14560, #14592, #15536).

Origin Session ID: 7ee47ccf-d1c7-469d-a75e-15cebf3b5ea5

Retrieval Hint: query_raw_memories("activity stream actor badge sender recipient chip family specimen")

tobiu referenced in commit 427d41d - "feat(fleet): activity rows name their actor and A2A sends their recipient (#17303) (#17351) on Aug 18, 2026, 5:41 PM
tobiu closed this issue on Aug 18, 2026, 5:41 PM