feat: Workflow builder rebuild + desktop fixes (the un-merged half of #140) - #142
Merged
Conversation
PipelineStudio rendered a per-agent provider chip from a hardcoded map (Requirement/Qa/Aggregate = anthropic, Coding/Testing = azure). A Force provider overrides every agent's own setting — LlmClientFactory resolves the forced key, not Agents:<Stage>:Provider — so while one is active that map does not merely go stale, it contradicts the run. Forcing RemoteAgent still rendered "Azure" next to Coding and Testing, telling the operator their work went to a cloud endpoint when every token had been produced by the CLI on their own machine. On a run whose entire point is "0 server tokens", that is the one label that must not lie. ProvName now returns the effective Force provider when there is one (read from the same LlmConfigView snapshot the "Live · provider" chip and the run itself use), and falls back to the per-agent map otherwise. Verified against the run that exposed it: with Force = RemoteAgent, the pipeline completed as Requirement (7 FR, 2 entities) -> Coding (17 files) -> Testing (21 tests, ~85% cov) -> QA (pass, 0.87) -> Aggregate at 0 tok / $0.0000 / 193.3s, every call dispatched to the paired runner's claude CLI. Release build 0 warnings / 0 errors; 847 tests pass, 14 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The leader row's chip was the literal "Claude" in markup, so after the previous commit the five specialist rows correctly read RemoteAgent while the Orchestrator above them still named Anthropic — and the leader row is the first thing a viewer reads. The colour class was also still driven by the hardcoded per-agent map, so a badge could read "RemoteAgent" while tinted as Azure. Both now derive from the effective config: LeaderProv returns the Force provider when one is set, and ProvCss maps any provider name onto the two badge classes that actually exist in app.css (.prov.anthropic / .prov.azure) rather than inventing a class no stylesheet defines — a phantom class renders unstyled, which design-system.md calls out as a past cause of broken windows. Verified on the running Aspire stack with Force = RemoteAgent: all six rows (Orchestrator + the five specialists) read RemoteAgent, matching the "RemoteAgent (forced)" toolbar chip. check-classes.ps1 reports no phantom classes (514 defined, all static .razor tokens resolve). Release build 0 warnings / 0 errors; 847 tests pass, 14 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
WindowHost rendered only `!w.Minimized && w.Workspace == ActiveWorkspace`. Blazor
disposes a component the moment it leaves the render tree, so minimizing a window
— or switching virtual workspace — threw away everything it held: a pipeline run
in flight, the story typed into it, the artifacts already produced. The
server-side work kept going with nowhere to land. On a desktop metaphor, minimize
must hide a window, not kill the app inside it.
The stylesheet had assumed this all along: `.appwin.minimized { display: none }`
has existed since the chrome was written, and was simply unreachable. WindowHost
now renders every open window and lets CSS hide it; AppFrame adds `off-workspace`
for a window parked on another workspace, with a matching rule next to the
minimized one.
Verified on the running Aspire stack: with a probe string typed into the Agents
window, clicking Minimize leaves the window mounted (`appwin focused minimized`,
`display: none`) with the textarea value intact — where it previously vanished.
check-classes.ps1 clean (515 defined, all static .razor tokens resolve); Release
build 0 warnings / 0 errors; 847 tests pass, 14 skipped.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Workflow editor put a 14-chip "Add step" strip across the top, floated the inspector over the node it was editing, and stacked new nodes on top of each other via a modulo formula — it read as a diagram toy, not a builder, and the user could not tell what a run produced without opening the separate Pipeline app. This restructures it into the three-region layout every serious builder (n8n, Google Opal, OpenAI Agent Builder) uses, and surfaces run results in place. Layout (G1): - LEFT — a searchable node library, grouped Agents / Flow / Data / Control, each node a card with icon, name and a one-line hint, colour-coded to the category it will paint on the canvas. Replaces the always-on chip strip (progressive disclosure — the palette is there when you want a node, not in your face). - CENTER — the canvas, with a proper zoom cluster (in / % / out / fit), the minimap, and an empty-state hint when there are no nodes. - RIGHT — a docked, persistent inspector that pushes the canvas rather than covering it; shows an empty-state when nothing is selected. - Toolbar slimmed to workflow-switch, name, live-provider chip, Save, Run, and an overflow "⋯" menu for Duplicate / panel toggle / Delete. Default window grown 1080×660 → 1320×840 so the canvas is actually usable. Run visibility (G3): - Each node carries a live status dot in its head (spinner while running, ✓ done, ✕ failed, – skipped) so progress reads on the card, not only in the log. - The docked inspector gains a Run section: selecting a node shows its status, its metric line (tokens / cost / elapsed) and, on failure, the reason — so "what did this node produce / why did it fail" is answerable without leaving the canvas. RunMessage is captured per node for that. Node insertion no longer stacks: a new node lands to the right of the selected (or rightmost) node on the same row, so it reads as the next step. Verified on the standalone Web with Node Playwright, light + dark: the three regions render correctly, the docked inspector populates on node-select, and a run lights every node's status dot (green ✓) with per-node token/cost meta plus a "RUN · DONE" section in the dock. Release build 0 warnings / 0 errors; check-classes.ps1 clean (553 defined, all static .razor tokens resolve); 847 tests pass, 14 skipped. Pushed the matching preview card to the Claude Design project (ui_kits/agentos/workflow.html). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eAgent path Running on a paired dev machine spends the member's own flat subscription, so the server cost is genuinely $0 — the whole point of the RemoteAgent provider. But the CLI reports no usage, so every run read "0 tok · $0.0000", which looks broken and hides the one number a demo wants: what this work would have cost on a metered API. Two honest additions, neither of which claims a billed spend: - RemoteAgentLlmClient now estimates token counts from the text it actually sent and received (~4 chars/token, the standard English heuristic) and reports them on the response + span. CostUsd stays 0m — the estimate is deliberately NOT folded into billed cost. "0 tok" becomes a real figure everywhere tokens are shown. - The Agents status bar accumulates, per call, what those tokens WOULD cost priced by the model each agent used (CostCalculator). When nothing was billed but tokens were spent, it shows "$0.0000 cost (≈ $X API)" — the saving, clearly labelled, never as actual spend. For real cloud providers the estimate equals the billed cost, so the parenthetical only appears on the $0 path. Verified on the standalone Web with Node Playwright: a pipeline run shows "1964 tok (1532↑ / 432↓) · $0.0000 cost (≈ $0.0071 API)". Two RemoteAgent tests that pinned the old "0 tokens" contract were updated to the new intent (zero cost, estimated tokens > 0). Release build 0 warnings / 0 errors; check-classes.ps1 clean; 847 tests pass, 14 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Building a workflow still meant clicking a node in the rail, then dragging an edge by hand — and there was no keyboard-first way to add a node at all. This adds the two interactions that make n8n / Opal feel fluid. Command palette (⌘K / Ctrl+K, or the toolbar "+ Add"): - A centred quick-add over the canvas. Type to filter every node type by name or hint, ↑/↓ to move the highlight, Enter to add, Esc to close. The global key is a document-level listener (workflow-keys.js) routed back to the component through a JSInvokable; registration is best-effort and CircuitSafe, so a missing script degrades to the "+ Add" button rather than crashing the circuit. Auto-connect: - Adding a node while one is selected now also draws the edge from it to the new node — the "add the next step" gesture — so a chain is one action per step instead of add-then-drag. The edge is written to the graph the same way BuildDiagram materialises persisted ones, so it survives Save/reload. Combined with the earlier no-stack positioning, new nodes land to the right of the anchor and wire up automatically. Verified on the standalone Web with Node Playwright: Ctrl+K opens the palette, typing "eval" narrows it to Evaluator with the keyboard highlight and ↵ hint, and adding a node while Testing Agent was selected wired Testing→Print with a visible edge. Release build 0 warnings / 0 errors; check-classes.ps1 clean (564 defined); 847 tests pass, 14 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…w builder
Two canvas-polish items from the builder rebuild.
Recent runs:
- The Recent Runs tab showed a bare counter that reset every time the window
closed, with the label "demo — history not persisted". It now records each run
(workflow name, time, completed/stopped, a one-line outcome) in the
OrchestrationStore, keyed per tenant and bounded to 25. The list is session-scoped
and in-memory — it survives closing and reopening the Workflow window, which the
old counter did not — and the label says exactly that ("this session — full
history lives in the Pipeline run store") rather than pretending to be durable.
Each row shows a green/red status dot, the name, the outcome and the timestamp.
Fit-to-view:
- "Fit" could zoom IN past 2.6× when the diagram library measured a small or
just-added bounding box, which looked broken. It is now clamped so fit never
exceeds 1:1 — it frames the whole graph or stops at 100%, never zooms in.
Verified on the standalone Web with Node Playwright: two runs produce two
timestamped rows in Recent Runs (badge "2", green dots), and Fit reframes the full
five-node graph at 56% instead of over-zooming. Release build 0 warnings / 0
errors; check-classes.ps1 clean (569 defined); 847 tests pass, 14 skipped.
Deferred: canvas undo/redo — the one remaining builder item, held back because a
correct command-history stack over the diagram is a larger, riskier change best
done on its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one deferred builder item. Snapshot-based, not a command stack: each structural edit deep-copies the whole graph (nodes + edges + positions) to JSON and pushes it, so undo is "restore this snapshot + rebuild the canvas" with no per-operation inverse to get wrong — the graph is already the serializable source of truth. Covers the operations that go through the app: add node, delete node, and the auto-connect that rides on an add. A manual canvas edge-draw or node drag folds into the next snapshot rather than being individually undoable, which keeps this free of re-entrant diagram-event coupling. History is bounded to 50. Toolbar gains undo/redo icon buttons (disabled when their stack is empty) and the global ⌘Z / ⌘⇧Z / ⌘Y shortcuts route through workflow-keys.js to OnUndoKey / OnRedoKey. One non-obvious fix this required: the DiagramCanvas was keyed by graph id alone, so restoring the SAME graph after an undo left the canvas bound to the previous BlazorDiagram instance and nothing re-rendered. The key now includes a version counter bumped on every BuildDiagram, so an undo/redo of the current graph forces a fresh canvas. Two undo/redo arrow icons added to the shared Icon component. Verified on the standalone Web with Node Playwright: adding a node takes the graph 5→6, ⌘Z returns it to 5, ⌘⇧Z back to 6 — with the toolbar buttons and minimap reflecting each step. Release build 0 warnings / 0 errors; check-classes.ps1 clean (569 defined); 847 tests pass, 14 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| <label>max iterations<input type="number" min="0" max="10" @bind="_selected.Data.MaxIterations" @bind:after="InspectorChanged" /></label> | ||
| </div> | ||
| @if (_selected.Data.Category == StepCategory.Evaluator) | ||
| @if (NodeLibrary.SelectMany(g => g.Types).Any(MatchesSearch) == false) |
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.
What & why
The RemoteAgent zero-cost path merged in #140, but that squash landed before the rest of the session's work was pushed. This PR brings the 8 commits that never reached
main: the desktop-correctness fixes and the full Workflow app rebuild. Branched off currentmainand cherry-picked cleanly (no conflicts); Release build 0/0, phantom-CSS clean (569 classes), 847 tests pass.Desktop / correctness fixes
WindowHostdropped minimized / off-workspace windows from the render tree, so Blazor disposed them (a running pipeline, typed input, produced artifacts — gone). Windows now stay mounted, hidden via CSS.Workflow app rebuilt (n8n / Opal-class visual builder)
Each item was verified in the running app (standalone Web via Node Playwright, light + dark) during the session — screenshots covered the three-region layout, docked inspector, per-node run status, the ⌘K palette, auto-connect, recent-runs, and undo/redo counts (5→6→undo 5→redo 6). Preview card pushed to the Claude Design project (
ui_kits/agentos/workflow.html).Type of change
Test plan
dotnet build AgentOs.slnx -c Release— 0 warnings, 0 errorsdotnet test AgentOs.slnx -c Release— 847 passed, 14 skippedcheck-classes.ps1— no phantom classes (569 defined)🤖 Generated with Claude Code