- Scope clickPill locator to data-message-role='user' bubble so the pill
button itself can no longer satisfy the dispatch guard
- Dedup clickPill retry: skip click if the user bubble already exists
- Hero pill: assert declarative-card count=0 (OSS-136 no-Card rule),
metric count >=4 (was >=3 — KPI strip is 4 tiles per composition rule)
- At-risk pill: assert no chart and no table testids (composition rule)
- Top-account pill: assert no data-table and no status-badge testids
- Rename hero test title to 'KPI strip + pie + bar (no surrounding card)'
so the title no longer falsifies the body
- QA docs: replace 'card + metrics + pie + bar' Expected Results with
'4 KPI metrics + 1 PieChart + 1 BarChart, no surrounding Card per OSS-136'
- Probe responseTimeoutMs derived from FIRST_SIGNAL_TIMEOUT_MS so it
matches the e2e 90s budget
Hero loses its surrounding card (bare KPI strip over the chart cards,
pinned to all six months); team performance pairs the rep table with a
quota-attainment bar chart; top account pairs the fact card with a
product-line pie (new dataset entry); at-risk becomes a risk panel — KPI
strip (ARR at risk / accounts / biggest exposure) over three side-by-side
severity cards with reason + next action. Fixtures re-captured from live
responses; D5 probe drops declarative-card from the hero set; e2e asserts
the accompanying charts and the risk panel; QA docs updated.
Ports beautiful-chat's exact visual language into the catalog renderers:
DashboardCard chrome (12px radius, 20px padding, soft shadow) for Card and
chart wrappers, its Metric typography with colored trend deltas, a recharts
donut (innerRadius 40, paddingAngle 2, tooltip, no legend) replacing the
custom SVG donut, and uniform blue bars on a dashed grid. E2E pie
fingerprints move from circle/legend assertions to recharts sectors; the
hero surface-count guard allows the two ResponsiveContainers (pie + bar)
one composed dashboard now produces.
Click a pill, then require the user-message bubble before asserting on
the surface; retry the click if it was swallowed. On slow dev-server
hydration the first click can land before the chat send pipeline is
wired, which previously burned the full surface-assertion budget and
masked the real failure point.
The demo now plays an embedded sales analyst for a fictional company:
suggestion pills are natural business questions (chart-type steering moved
from user prompts into the system prompt), the hero pill composes a full
dashboard (KPI metrics + pie + bar in one surface) modelled on
beautiful-chat's sales dashboard, and the catalog gains DataTable,
gap-aware Row/Column, Metric trendValue, and the beautiful-chat palette.
Dataset + composition rules ship as frontend agent context
(sales-context.ts) so they reach both the primary agent and the secondary
A2UI planner in LGP and ADK alike. E2E specs and QA docs updated to the
new pill set.
The two tests were skipped (W8-7) under the assumption that Railway
agent slowness caused timeouts. The actual root cause was twofold:
1. Fixture content+toolCalls split (already fixed in 2436adba6 for all
four pills including KPI and StatusReport).
2. CSS selector mismatch: the tests used inline-style selectors
(letter-spacing: 0.12em, border-radius: 999) but the renderers use
Tailwind classes (tracking-wider, rounded-md). Switched both tests
to use the data-testid attributes already present on the components
(declarative-metric, declarative-status-badge).
Verified 6/6 pass on both LGP (3100) and LGT (3101). LGP and LGT
test specs are byte-identical.
Three classes of regression are now pinned:
1. Secondary-LLM tool name doesn't collide with the A2UI middleware's
default intercept list (`render_a2ui`). New
`src/agents/test_a2ui_internal_tools.py` parametrises over
`beautiful_chat._design_a2ui_surface`, `a2ui_dynamic._design_a2ui_surface`,
and `a2ui_fixed.display_flight` and asserts none match the
middleware's `a2uiToolNames` default. Catches accidental rename
reverts that would re-enable the bypass.
2. `generate_a2ui` force-pins the canonical `catalog_id` even when the
secondary LLM hallucinates a wrong one. The new test stubs
`ChatOpenAI` with a fake response carrying a bogus catalogId and
asserts the surface op carries the module's `CUSTOM_CATALOG_ID`.
3. `generate_a2ui` short-circuits with a clean error string when the
LLM emits a root component without a `component` field — never
feeds the renderer the partial tree that surfaced as the "Cannot
create component root without a type" infinite-loop.
7 unit tests, all green locally (`pytest src/agents/test_a2ui_internal_tools.py`).
E2E tests on the same fixes now also assert:
- No `A2UI render error: Catalog not found` banner on the page after
Beautiful Chat → Sales Dashboard, Declarative Gen UI → BarChart, and
A2UI Fixed Schema → Find SFO → JFK round-trips.
- No `Cannot create component … without a type` banner on the same
three pills.
- Exactly ONE flight card on A2UI Fixed Schema (was 6+ on deploy
pre-fix from the `display_flight` loop) — `Flight Details` count
pinned to 1, `Book flight` count pinned to 1.
- At most one ResponsiveContainer on the BarChart pill (loops would
stack multiple).
- At most two ResponsiveContainers on Beautiful Chat → Sales Dashboard
(one pie + one bar = single dashboard render).
The showcase framework directories better reflect their role as
integration examples rather than distributable packages.
Renames showcase/packages/ -> showcase/integrations/ and updates
the test docker-compose file reference accordingly.