feat(render): single render layer — typed AgentResponse + zedup state hues - #34
Merged
Conversation
- 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.
There was a problem hiding this comment.
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
AgentResponsesealed taxonomy (+ mapping fromPipelineEvent) 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
startIndexinjection, 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 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 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 | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
AgentResponsetaxonomy (Plan/Progress/Question/Result/Blocker/Handoff/Replan/Action) + Speaker + Subtask; continuous gutter blocks coloured by zedupStateHue(claudart owns the StateHue→ANSI bridge), header+gutter only; dependency-ordered Plan with colour-coded subtask titles; every command header routes throughrender.header;runFuturerenders typed blocks live, spinner kept as the active pulse.840 tests green, build clean.