Files
podman/docs/stream-categorization.md
Ramis aeb75c310e feat: split pod streams into Signals vs Reasoning sections
My/Team streams were one flat lane; source provenance was buried as a
gray badge by the filename. Group each lane into Signals (vision/git
inputs) and Reasoning & decisions (collision/intervention/outcome), and
promote source to a color-coded provenance chip with a readable kind
label. Presentation-only; no shared-type or backend changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VpySDjxpM1usXMEngJRWqG
2026-06-27 22:08:53 -07:00

4.3 KiB

Stream Categorization — My Stream / Team Stream

Why

Judges must read the pod stream in 10 seconds. Right now both lanes (My stream, Team stream) dump every event into one flat chronological list. The two things that prove "self-improving agent" are mushed together:

  • Sources of decisions — raw signals the agent observed (screen vision, git).
  • Reasoning decisions — what Hermes concluded and did (conflict detected, intervention spoken, outcome/verifier result).

source (vision/git/memory/hermes/policy) is currently buried as a plain outline badge next to the filename. kind is only an icon. No sectioning. Result: bloated, undifferentiated, doesn't tell the loop story (observe → reason → act → learn).

Goal

Split each stream lane into clear sections and promote provenance, so a judge sees: "agent ingests signals, then makes reasoning decisions from them."

No backend / shared-type changes. The data already carries kind + source. Pure presentation change in frontend/src/components/PodView.tsxActivitySidebar + ActivityItem + new helpers only. Additive, localized.

Categorization (the contract)

Two sections, derived from existing PodActivityKind:

Section Heading kinds Meaning
signal Signals observation, git Raw inputs the agent saw. The sources.
decision Reasoning & decisions collision, intervention, outcome What Hermes reasoned and did.
const CATEGORY_OF: Record<PodActivityKind, 'signal' | 'decision'> = {
  observation: 'signal',
  git: 'signal',
  collision: 'decision',
  intervention: 'decision',
  outcome: 'decision',
};

Section order: Signals first, Reasoning & decisions second (top-to-bottom = the loop direction). A section with zero events renders nothing.

Provenance chip (sources)

Promote source to a leading color-coded chip with an icon. This is the "source of decision" tag judges look for.

source label icon tint
vision Vision EyeIcon chart-1
git Git GitBranchIcon chart-2
memory Memory BrainIcon chart-4
hermes Hermes SparklesIcon primary
policy Policy ShieldIcon chart-3

Chip class pattern (use variant="outline" so the default primary fill is overridden): border-{tint}/40 bg-{tint}/10 text-{tint}.

Kind label

Render kind as readable text next to the provenance chip, not just an icon:

kind label
observation Observed
git Git
collision Conflict
intervention Intervention
outcome Outcome

Tag row order in each card: source chip → kind label → actors → file.

Implementation steps (after teammate lands frontend work)

  1. Rebase / pull teammate's PodView.tsx first. Do not start before it lands — this file is being actively rewritten right now.
  2. Add icon imports: BrainIcon, EyeIcon, ShieldIcon, WorkflowIcon (and reuse GitBranchIcon, SparklesIcon, RadioTowerIcon). Import PodActivitySource type from @podman/shared.
  3. Add module-level consts: CATEGORY_OF, CATEGORIES (id/label/hint/icon), SOURCE_META (label/icon/className), KIND_LABEL.
  4. In ActivitySidebar's expanded SidebarContent, replace the flat events.map(...) with a CATEGORIES.map(...) that filters events per category, skips empty sections, and renders a section header (icon + label + count + hint) above each group.
  5. In ActivityItem, replace the buried source <Badge variant="outline">{source}</Badge> with the colored provenance chip + a kind-label badge; keep actors and file badges.
  6. Collapsed icon-rail (the group-data-[collapsible=icon] mini list) stays flat — no sectioning needed there.
  7. Verify: pnpm --filter @podman/frontend build typechecks; empty-state and a live pod with mixed events both render correctly.

Out of scope (do not do)

  • No changes to shared/src/activity.ts, the SSE hook, or backend event emission.
  • No new event kinds/sources.
  • No third section / per-kind lanes — two buckets is the whole point (signals vs reasoning). Keeps a sparse demo stream from fragmenting.

Merge-safety note

Single file, two functions. Hold until the teammate improving the frontend pushes, then pull and apply on top to avoid clobbering their design pass.