refactor(review): consolidate one-row-per-agent collapse + drill-in polish · Entire

refactor(review): consolidate one-row-per-agent collapse + drill-in polish

d2707ba·

peyton-alt·3d ago·5 files·+282 added/-130 removed

Pre-merge cleanup of the agent-row collapse, which had accreted across three commits as scattered TUISink fields/methods. Same behavior, one owner, plus the two facets found by reading rather than design.

(c) New agentGrouping type owns the whole reconciliation between per-worker execution and one-row-per-agent display: worker->row routing, live per-agent token summing, and the summary fold. TUISink holds one *agentGrouping (nil on the single-agent path) instead of four fields and four methods.

(a) The folded summary's duration now spans earliest-worker-start to latest-worker-end instead of keeping one worker's slice. The live row duration was already correct (wall-clock from first event to finish); this makes the folded field honest for any future consumer.

(b) Drill-in (Ctrl+O) into a collapsed agent tags each event with its skill when the buffer holds more than one worker, so two skills running in parallel read cleanly instead of interleaving anonymously; single-source buffers stay untagged. agentEventMsg carries the source worker; the row buffer keeps it per event.

Execution, summary, manifest, and per-skill session attribution are untouched — the collapse remains display-only.

Sessions

01KXH6FFA9B5JWVHSRGMH1W20BView transcript

Changes

5

// Package review — see env.go for package-level rationale.
package review

import (
    "time"

reviewtypes "github.com/entireio/cli/cmd/entire/cli/review/types"
)

type agentGrouping struct {
    rowOrder      []string            // per-agent row labels, in display order
    workerToAgent map[string]string   // worker label → agent row
    rowWorkers    map[string][]string // agent row → its worker labels
    workerTokens  map[string]reviewtypes.Tokens
}

func newAgentGrouping(rowOrder []string, workerToAgent map[string]string) *agentGrouping {
    g := &agentGrouping{...}
    return g
}

// rowFor resolves a worker label to its agent row, passing through any name absent from the map.
func (g *agentGrouping) rowFor(name string) string {
    if row, ok := g.workerToAgent[name]; ok {
        return row
    }
    return name
}

// liveTokens records worker's latest cumulative Tokens... 
func (g *agentGrouping) liveTokens(worker string, tk reviewtypes.Tokens) reviewtypes.Tokens {
    ...
}

// collapseSummary folds a per-worker summary into one AgentRun per agent row.
func (g *agentGrouping) collapseSummary(summary reviewtypes.RunSummary) reviewtypes.RunSummary {
    ...
}

// reviewStatusWorse reports whether a is a worse terminal status than b.
func reviewStatusWorse(a, b reviewtypes.AgentStatus) bool {
    ...
}

...
// buildEventLines returns every wrapped body line for the supplied event buffer, in order.
func buildEventLines(buffer []reviewtypes.Event, maxWidth int) []string {
    ...
}

// multiSource reports whether a buffer holds events from more than one distinct worker.
func multiSource(buffer []bufferedEvent) bool {
    ...
}

// skillTag reduces a worker label to its skill for drill-in tagging:
func skillTag(worker string) string {
    ...
}

// detailFrame renders the alt-screen drill-in chrome around a body string.

// TestTUISink_CollapsesSummaryToAgentRows tests that the TUISink can correctly collapse the summary. func TestTUISink_CollapsesSummaryToAgentRows(t *testing.T) { ... }

// TestAgentGrouping_CollapseAggregatesDurationSpan tests the aggregation of duration across collapsed runs. func TestAgentGrouping_CollapseAggregatesDurationSpan(t *testing.T) { ... }

// TestBuildEventLines_LabelsSkillWhenMultipleSources tests that events from multiple sources are correctly labeled. func TestBuildEventLines_LabelsSkillWhenMultipleSources(t *testing.T) { ... }