Skip to content

fix: close AG-UI audit gaps F1–F4 + F6 (composer clear, light scheme, graceful stop, json-render values, markdown tracking)#663

Merged
blove merged 17 commits into
mainfrom
ag-ui-gap-closure-p3
Jun 12, 2026
Merged

fix: close AG-UI audit gaps F1–F4 + F6 (composer clear, light scheme, graceful stop, json-render values, markdown tracking)#663
blove merged 17 commits into
mainfrom
ag-ui-gap-closure-p3

Conversation

@blove

@blove blove commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Phase 3 of the AG-UI demo toolbar campaign — closes the in-scope gaps from the Phase 2 capability audit (docs/superpowers/specs/2026-06-11-ag-ui-capability-findings.md, plan in docs/superpowers/plans/2026-06-11-ag-ui-gap-closure-p3.md). Each fix landed TDD-first with two-stage review, and the whole branch was re-verified in a live Chrome smoke against a real OpenAI backend.

Fixes

  • F1 — composer kept sent text (@threadplane/chat): chat-input now binds [value]/(input) directly; NgModel never wrote the programmatic clear back to the view under zoneless + OnPush (reproduced on the production canonical demo). FormsModule dropped; @angular/forms peer dependency removed (CHANGELOG'd). name="messageText" preserved for cockpit selectors. New unit + pressSequentially e2e regressions.
  • F2 — light scheme didn't reach the page chrome (examples/ag-ui): AgUiShell now sets data-color-scheme and index.html gained the canonical pre-bootstrap script; e2e-pinned.
  • F3 — stop rendered an error banner (@threadplane/ag-ui): user aborts settle as graceful cancellation on both delivery paths — onRunFailed and the synthesized RUN_ERROR event (the second path was caught only by the live smoke). Also fixes a stop→regenerate→stop sequence that wedged the loading state (flags now reset in regenerate() too).
  • F4 — json-render [object Object] values (shared, also reproduced on prod): the schema documents {statePath} bindings but only the A2UI path translated them; chat-generative-ui now normalizes them to the $bindState dialect + _bindings and seeds spec state. Consumer-visible semantics change (CHANGELOG'd): json-render surfaces are store-isolated per instance — pass an explicit [store] for backend-state-driven or shared dashboards (both cockpit dashboard capabilities now do; chat-tool-views keeps the previous shared-store behavior). Composition-level test pins the isolation.
  • F6 — NG0956 during streaming (@threadplane/chat): markdown children/table rows track by $index (re-parsed nodes are new objects every delta). Residual warnings during json-render spec assembly are logged as follow-up in the findings doc.

Verification

  • nx run-many -t lint,test,build -p chat,ag-ui,render green (chat 845+, ag-ui 30 incl. new wedge/abort tests)
  • nx build examples-ag-ui-angular + full ag-ui e2e green (18 specs incl. 2 new regressions); cockpit json-render e2e green (restored STATE_SNAPSHOT KPI assertion)
  • Live Chrome smoke vs real backend: light chrome ✅, composer clears ✅ (incl. mid-stream), stop with no banner ✅, dashboard renders real values ✅, interrupt Accept round-trip ✅

Follow-ups (logged in the findings doc): F5 subagent card over AG-UI (Phase 4 design), residual NG0956 in spec assembly, a2ui icon catalog support.

🤖 Generated with Claude Code

blove and others added 14 commits June 11, 2026 21:08
… gap is the error banner

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…page chrome (F2)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ngModel does not write a programmatic clear back to the view under
zoneless + OnPush, so sent text stayed in the composer. Bind
[value]/(input) directly and drop FormsModule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… (review)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
abortRun() makes the AG-UI client report onRunFailed('BodyStreamBuffer
was aborted'), which rendered a red error banner for a user-initiated
stop. Track abort intent and settle the store as idle instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-parsed markdown produces new child objects every stream delta;
identity tracking re-created the DOM subtree per chunk (NG0956).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The json-render schema prompt (examples/*/python/src/schemas/json_render.py)
documents state-bound props as { statePath: "/path" } with spec.state as
the initial value model, but @json-render/core only resolves $state/
$bindState expressions — the raw object fell through to the a2ui catalog
components and interpolated as the literal string "[object Object]"
(KPI Text values, Slider labels, TextField values, Overview metrics).

chat-generative-ui now (a) normalizes { statePath: p } props to the
engine-native { $bindState: p } plus a _bindings map (mirroring what
surfaceToSpec does for A2UI path refs, so user input writes back), and
(b) seeds spec.state into the provided store — the chat composition's
shared store starts empty and render-spec only self-seeds when no store
input is given. User-modified paths are never clobbered on spec re-emits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ing (review)

Passing the conversation-wide internal store into every generative-ui
surface made same-key dashboards collide across messages (regenerate,
multi-dashboard, thread switch). Match a2ui isolation: only an explicit
consumer store is shared; otherwise render-spec self-seeds per instance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mposition isolation (review)

The store-isolation fix means backend-state sync reaches json-render
surfaces only via an explicit consumer store; the two cockpit dashboard
capabilities want exactly those shared live semantics, so they now opt
in. Adds a composition-level test pinning the isolation binding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chat-input no longer uses ngModel (F1); dependency-checks lint flagged
the stale peer. Consumer-visible: @threadplane/chat no longer requires
@angular/forms.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The AG-UI client surfaces a user abort through the event stream as
RUN_ERROR in addition to onRunFailed; the event path bypassed the
graceful-stop guard, so the error banner still appeared in live smoke.

Introduces an abortSettled flag alongside abortRequested: the first
delivery (event or onRunFailed) settles the store as idle and sets
abortSettled; any subsequent delivery for the same abort is swallowed
by settleIfAborted() without re-touching the store. Both flags are
reset together at the top of submit() so the next run starts clean
and genuine failures after a previous stop are never suppressed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ls logged

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… defensively (review)

stop -> regenerate -> stop left the store wedged in streaming because
regenerate never reset the abort flags and the duplicate-delivery guard
returned without settling. Also documents the json-render store-isolation
change in the chat CHANGELOG and fixes the README peer table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadplane Ready Ready Preview, Comment Jun 12, 2026 2:12pm

Request Review

@blove blove merged commit 3fd150f into main Jun 12, 2026
19 checks passed
blove added a commit that referenced this pull request Jun 12, 2026
…g update (#664)

Phase 3 (#663) updated the cockpit json-render graph docstring and guide;
the Railway drift guard requires the generated bundle to match.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant