Skip to content

feat(render): single render layer — typed AgentResponse + zedup state hues - #34

Merged
liitx merged 2 commits into
mainfrom
feat/agent-response-render-layer
Jun 18, 2026
Merged

feat(render): single render layer — typed AgentResponse + zedup state hues#34
liitx merged 2 commits into
mainfrom
feat/agent-response-render-layer

Conversation

@liitx

@liitx liitx commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Single render layer for all agent output.

fix (unblock): revert stale zedup wiring in link.dart (#15, no longer compiles against zedup main); realign drifted tests (menu startIndex, approvalSelector injection, README-rewrite reconciliation, tearDownAll coverage).

feat: typed AgentResponse taxonomy (Plan/Progress/Question/Result/Blocker/Handoff/Replan/Action) + Speaker + Subtask; continuous gutter blocks coloured by zedup StateHue (claudart owns the StateHue→ANSI bridge), header+gutter only; dependency-ordered Plan with colour-coded subtask titles; every command header routes through render.header; runFuture renders typed blocks live, spinner kept as the active pulse.

840 tests green, build clean.

liitx added 2 commits June 18, 2026 06:34
- link.dart: revert the stale zedup projection wiring (#15) that no longer
  compiles against zedup main's ZedupConfig/emit API — restores the build.
- teardown_test, e2e_smoke: match the menu's current {int startIndex} pickFn.
- agent_pipeline_test: inject approvalSelector so the flow gate needs no TTY.
- readme_sync_test: reconcile with the narrative README rewrite — keep the
  command-routing and file-reference guards, drop the abandoned glossary.
- claudart_matrix: assert coverage in tearDownAll so it survives randomization.
…hues

- AgentResponse sealed taxonomy (Plan/Progress/Question/Result/Blocker/Handoff/
  Replan/Action) + Speaker + Subtask; never-guess and questions-float encoded
  structurally and asserted by matrix tests.
- render.dart: the single owner of agent output. Continuous gutter blocks
  coloured by zedup StateHue (claudart owns the StateHue->ANSI bridge), header
  and gutter only, body plain. Dependency-ordered Plan with colour-coded
  subtask titles and a done count.
- Shared primitives (header/status/field/StatusBadge); every command header now
  routes through render.header.
- event_response_map: PipelineEvent -> AgentResponse; runFuture renders typed
  blocks live (Result/Blocker/Question), spinner kept as the active pulse.
- docs/agent_response_and_output.md design entry.
Copilot AI review requested due to automatic review settings June 18, 2026 11:35
@liitx
liitx merged commit a1c14b8 into main Jun 18, 2026
1 check passed
@liitx
liitx deleted the feat/agent-response-render-layer branch June 18, 2026 11:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes CLI output formatting into a single render layer and introduces a typed AgentResponse hierarchy to standardize agent/subagent emissions, while also realigning tests and reverting stale zedup link wiring that no longer compiles.

Changes:

  • Add AgentResponse sealed taxonomy (+ mapping from PipelineEvent) and render all pipeline events as typed guttered blocks.
  • Introduce shared render primitives (header, status, field) and migrate multiple commands to the unified header formatting.
  • Update/realign tests (new render tests, menu startIndex injection, readme sync adjustments, matrix gap assertion timing).

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test/ui/render_test.dart Adds coverage for rendering invariants (header first, no ANSI in non-TTY, question ordering, plan/action specifics).
test/ui/render_primitives_test.dart Tests shared formatting primitives (badge/header/status/field) and non-TTY ANSI stripping.
test/readme_sync_test.dart Re-scopes README sync checks to command routing + prose file references (Roadmap excluded).
test/pipeline/event_response_map_test.dart Verifies PipelineEvent → AgentResponse mapping matrix behavior.
test/pipeline/agent_response_test.dart Tests AgentResponse taxonomy invariants (labels, sort priorities, hues, never-guess).
test/pipeline/agent_pipeline_test.dart Injects approval selector for non-TTY testability.
test/matrix/claudart_matrix.dart Moves matrix gap assertion to tearDownAll for randomized test ordering.
test/e2e_smoke_test.dart Updates pickFn stubs to match new startIndex menu signature.
test/commands/teardown_test.dart Updates _pick stub signature to accept optional startIndex.
lib/ui/render.dart New single render owner: block gutter rendering + primitives + per-response formatting.
lib/ui/ansi.dart Adds additional ANSI color constants used by the new render layer.
lib/pipeline/pipeline_executor.dart Renders typed blocks live in runFuture, clears spinner line before rendering blocks.
lib/pipeline/event_response_map.dart New bridge mapping executor events to user-facing AgentResponse blocks (or null).
lib/pipeline/agent_response.dart New sealed AgentResponse taxonomy (+ speaker/state models and ordering helpers).
lib/commands/unlink.dart Migrates header printing to render.header.
lib/commands/teardown.dart Migrates header printing to render.header.
lib/commands/suggest.dart Migrates headers to render.header and removes local header helper.
lib/commands/status.dart Migrates header printing to render.header.
lib/commands/setup.dart Migrates header printing to render.header.
lib/commands/scan.dart Migrates header printing to render.header.
lib/commands/save.dart Migrates header printing to render.header.
lib/commands/rotate.dart Migrates header printing to render.header.
lib/commands/report.dart Migrates header printing to render.header.
lib/commands/link.dart Reverts/removes stale zedup codegen wiring; migrates header printing to render.header.
lib/commands/launch.dart Migrates header printing to render.header.
lib/commands/kill.dart Migrates header printing to render.header.
lib/commands/init.dart Migrates header printing to render.header.
lib/commands/flow.dart Migrates headers/classification formatting to render.* and removes local header helper.
lib/commands/debug.dart Migrates headers to render.header and removes local header helper.
lib/commands/archives.dart Migrates header printing to render.header.
docs/agent_response_and_output.md Adds design documentation for the typed response/output layer.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/ui/render.dart
Comment on lines +5 to +10
// Each `AgentResponse` renders as a block with a continuous coloured gutter
// (`│`) and a coloured header; the body stays plain. The colour is the
// response's state `hue` (a zedup `StateHue`), translated to ANSI here — claudart
// prints ANSI, zedup's StateHue.color is a nocterm Color, so this layer owns the
// `StateHue → ANSI` bridge. Colour carries meaning on the header + gutter only,
// so the body stays readable and greppable, and ANSI strips cleanly when piped.
Comment thread lib/ui/render.dart
Comment on lines +95 to +105
case Question(:final origin, :final workspace, :final blockedSubtask, :final question, :final options):
final body = <String>[
question,
for (final o in options) ' - $o',
ansi.c(ansi.dim, 'answer to unblock $blockedSubtask'),
];
return block(
r.hue,
_headerLine(r, 'from $origin · $workspace / $blockedSubtask'),
body,
);
Comment on lines +65 to 68
final refs = RegExp(r'[A-Za-z0-9_/]+\.dart')
.allMatches(prose)
.map((m) => m.group(0)!.split('/').last)
.toSet();
Comment on lines +16 to +23
## Two speakers

Every response carries a `Speaker` so the render layer can separate who is talking:

- **claudart** — the foreground voice you chat with. Always responsive, never blocked. Its
own color and header.
- **Agent** — the background orchestrator running subagents. Its own lane.
- **Subagent** — a worker inside the Agent, labeled by workspace/subtask.
Comment on lines +37 to +45
| Variant | Fields | Primary render |
|---------|--------|----------------|
| **Plan** | goal, subtasks[], ordered by priority | priority list + relationship tree |
| **Progress** | workspace, subtask, AgentFlow state, blocked/unblocked | status table + state icon |
| **Question** | origin, workspace, blockedSubtask, question, options[] | pinned callout |
| **Result** | workspace, subtask, filesTouched[], summary | collapsed card |
| **Blocker** | workspace, step, errorType | red row |
| **Handoff** | from, to, resolvedInfo | dim one-liner |
| **Replan** | reason, oldOrder, newOrder | diff list |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants