Commit Graph

142 Commits

Author SHA1 Message Date
github-actions[bot] b13129b319 style: auto-fix formatting 2026-05-07 19:08:48 +00:00
Alem Tuzlak 26bf858da1 Merge cr-fix-1/F5 into blitz/lgp-genuine-pass/integration 2026-05-07 21:05:45 +02:00
Alem Tuzlak cdc3bf024c Merge cr-fix-1/F4 into blitz/lgp-genuine-pass/integration 2026-05-07 21:05:45 +02:00
Alem Tuzlak 50118da774 Merge cr-fix-1/F3 into blitz/lgp-genuine-pass/integration 2026-05-07 21:05:44 +02:00
Alem Tuzlak 7a93369bb7 Merge cr-fix-1/F2 into blitz/lgp-genuine-pass/integration 2026-05-07 21:05:44 +02:00
Alem Tuzlak 8af17e7369 chore(showcase/aimock): tighten cosmic-gradient matcher and refresh d5-all header
Two small hygiene fixes:

- frontend-tools cosmic-gradient pill matched bare token 'navy' which
  could collide with any prompt mentioning navy. Tighten to
  'navy → magenta cosmic gradient' — verbatim substring of the
  pill prompt in frontend-tools/suggestions.ts. Mirror into d5-all.json.
- d5-all.json header comment claimed the first 7 entries are the
  open-gen-ui pills, but ordering shifts as features land (headless-
  simple/complete pills currently sit ahead of open-gen-ui after
  recent merges). Rewrite the header to describe the ordering
  convention generically: high-priority verbatim-prompt fixtures
  appear first, first-match-wins; per-fixture _comments and the
  per-feature source files are the source of truth.
2026-05-07 21:04:19 +02:00
Alem Tuzlak ce193f24b8 chore(showcase/aimock): sync frontend-tools-async source file with d5-all.json
The per-feature source file owned only 2 fixtures (one matcher,
'project planning') while the bundled d5-all.json had 6 fixtures
(3 pills × 2 turns each: project-planning, auth, reading). Because
d5-all.json is auto-merged from the per-feature sources, any future
regen would wipe the auth and reading fixtures from the bundle and
silently regress the frontend-tools-async probe.

Copy all 6 fixture entries from d5-all.json into
frontend-tools-async.json so the source is authoritative. No change
to d5-all.json — the bundle already has these entries.
2026-05-07 21:03:46 +02:00
Alem Tuzlak 82e699b544 chore(showcase/aimock): branch gen-ui-declarative fixture per-pill
The render_a2ui matcher emitted the same Card+Metric payload for every
pill, so the d5-gen-ui-declarative probe went red on the second pill —
its per-pill expectedTestIds map demands declarative-pie-chart for the
pie-chart pill, declarative-bar-chart for the bar-chart pill, and
declarative-status-badge for the status-report pill, none of which
were rendered.

Branch the render_a2ui response by combining userMessage substring with
toolName so each pill emits the catalog component its probe expects:

- KPI dashboard       → Card + 3 Metric children
- pie chart           → PieChart with regional sales data
- bar chart           → BarChart with quarterly revenue data
- status report       → Card + 3 StatusBadge children

Keep the bare toolName-only matcher at the bottom as a deterministic
fallback so unforeseen pills still render something instead of erroring.
Mirror all five fixture entries into the bundled d5-all.json.
2026-05-07 21:03:33 +02:00
Alem Tuzlak 54715c8e7d chore(showcase/aimock): align headless-complete Highlight and AAPL fixture matchers with pill prompts
Two pill mismatches were silently routing the headless-complete Stock and
Highlight pills to the showcase-assistant catch-all in feature-parity.json:

- The Stock fixture matched 'AAPL trading' but the SuggestionBar pill
  configured by use-headless-suggestions.ts sends 'What's the price of
  AAPL right now?' — substring 'AAPL trading' is not in that prompt.
- The Highlight fixture matched 'Highlight \'meeting at 3pm\'' but neither
  the empty-state pill ('Highlight: ship the demo on Friday') nor the
  SuggestionBar pill ('Highlight this note for me: ...ship the demo on
  Friday...') contains that substring.

Switch both matchers to short distinctive substrings ('AAPL' and
'ship the demo on Friday') that appear verbatim in BOTH the empty-state
and SuggestionBar prompts. The tool-rendering AAPL fixture (matcher
'What\'s the current price of AAPL?') stays at higher priority via
array order — first-match-wins keeps it pinned to the tool-rendering
pill, so substring 'AAPL' here only catches headless-complete pills.
Update narration response text to reference the correct highlighted
phrase.
2026-05-07 21:03:00 +02:00
Alem Tuzlak 5fc45ee144 fix(showcase/harness): per-pill conjunctive testid check for declarative D5
Two related issues in the per-pill assertions:

  1. The status-report pill listed BOTH `declarative-status-badge` and
     `declarative-card` as expected testids and used `.some()` to pass.
     Pill 1 (kpi-dashboard) already mounts `declarative-card`, so the
     status-report pill trivially passed via leftover even when it
     produced no status badge at all.
  2. All pills used `.some()` (any-of), so mounting a single satisfying
     testid was enough — even one that was leftover from an earlier pill.

Switch the per-pill expected-testid check to `.every()` (conjunctive)
and require the status-report pill's distinguishing testid
(`declarative-status-badge`) explicitly. Add a per-pill baseline
captured via the conversation-runner's `preFill` hook so the assertion
also requires at least ONE expected testid to be NEWLY mounted (not
just present in the DOM as cross-pill leftover).
2026-05-07 21:02:37 +02:00
Alem Tuzlak f6aeeac5b4 fix(showcase/harness): per-pill baseline for gen-ui-agent D5
`[data-testid="agent-step"]` rows accumulate across pills — pill N's
rows persist into pill N+1's DOM. The previous absolute-count check
(`stepCount >= 2`) trivially passed on pill 2/3 even when the pill
produced no new rows; the cumulative-text duplication check was the
only signal catching cross-pill regressions.

Use the conversation-runner's `preFill` hook to capture the agent-step
DOM state BEFORE each pill is sent, then assert the count grew by at
least 2 NEW rows (delta-based, not absolute). The cumulative-text
fingerprint check is kept as a secondary signal to catch fixtures that
return the same canned step list across prompts.
2026-05-07 21:02:23 +02:00
Alem Tuzlak ef5e381165 fix(showcase/harness): per-pill baseline for shared-state-streaming D5
`[data-testid="document-content"]` is sticky DOM that carries text from
pill N into pill N+1's pre-fill state. The previous assertion only
checked the absolute final char count, so pill 2/3 trivially passed on
pill 1's leftover document content even when the stream regressed.

Use the conversation-runner's `preFill` hook to capture the document
content baseline (text + char count) BEFORE each pill is sent, then
after settle assert either:
  - delta from baseline >= STREAMING_MIN_FINAL_CHARS (substantive new
    content appended), OR
  - text differs from baseline AND final char count >= threshold
    (document was REPLACED with substantive content of similar size).

Drop the misleading "guards against single-chunk emission" claim from
the docstring — the assertion runs after the runner's settle window,
so mid-stream chunking is NOT observable here. Documented as a
follow-up.
2026-05-07 21:02:10 +02:00
Alem Tuzlak 49ca8fd1d9 fix(showcase/harness): drive real MCP-tool prompt for mcp-apps D5
The probe input was "hello" with a misleading comment claiming it was a
no-op. The MCP-apps demo only mounts a sandboxed iframe AFTER an MCP
tool call returns a UI resource — `MCPAppsActivityRenderer` subscribes
to the runtime's MCP activity event and dynamically appends the iframe;
"hello" never triggers that path, so the iframe assertion was either
trivially red or trivially green via DOM leftovers.

Switch the input to the verbatim suggestion-pill prompt
"Open Excalidraw and sketch a system diagram with a client, server,
and database." (from `langgraph-python/src/app/demos/mcp-apps/suggestions.ts`)
and document honestly that the canned aimock fixture currently keyed to
"Use Excalidraw to sketch" returns content-only (no MCP tool call), so
the iframe assertion can only pass under a live agent + reachable MCP
endpoint until the fixture is updated. F4 owns the fixture file — TODO
left in the comment.
2026-05-07 21:01:53 +02:00
Alem Tuzlak 4ef38a64ce fix(showcase/harness): use fixture-keyed prompt for gen-ui-open-advanced D5
The probe sent "render the advanced gen-ui sandbox" — a string with no
fixture entry in `showcase/aimock/d5-all.json` or
`showcase/harness/fixtures/d5/gen-ui-open.json`, so the iframe never
rendered through a deterministic tool call. Switch to the verbatim
suggestion-pill prompt "Inline expression evaluator" which is keyed to
a `generateSandboxedUi` tool call in d5-all.json (line 77).

Also tighten the iframe selector cascade: the bare `iframe` fallback
matched ANY iframe on the page (e.g. third-party analytics frames).
Replace with `iframe[sandbox*="allow-scripts"]`, matching the advanced
demo's actual sandbox spec.
2026-05-07 21:01:36 +02:00
Alem Tuzlak bda691bd30 fix(showcase/harness): dwell-and-recheck d5-subagents chain to catch critic loop regressions
The probe returned at the first snapshot with critic count == 1, so a
regression that briefly shows 1 critic card before the supervisor
re-invokes critique_agent (count grows to 2) would silently pass.

After the validator passes, hold for CHAIN_SETTLE_DWELL_MS (3s) and
re-snapshot. If the second snapshot fails validation, including a
critic count > 1 or the new sub-agent-empty sentinel appearing in
the page text, throw with a clear destabilised-during-dwell message
that surfaces both initial and post-dwell counts.

Adds the empty-sub-agent sentinel from subagents.py to the probe
BOILERPLATE_MARKERS so an empty sub-agent result fails the test
instead of rendering as an empty card.
2026-05-07 20:55:49 +02:00
Alem Tuzlak ac9ff8affd fix(showcase/harness): use numeric comparator when sorting status codes
[first.status, second.status].sort() defaults to lexicographic
ordering — it returned [200, 429] correctly only by accident and
would mis-order [200, 1000]. Pass an explicit (a,b)=>a-b comparator
so the assertion holds for any future status-code pair.
2026-05-07 20:55:24 +02:00
Alem Tuzlak 17d98f5386 fix(showcase/harness): drop reasoning-display keyword fallback
The probe sent "show your reasoning step by step" and accepted any
assistant transcript containing "reasoning", "step", or "thinking".
Those tokens are typical assistant acknowledgements of the prompt
itself, so the keyword fallback always matched regardless of
whether the framework actually surfaced REASONING_MESSAGE_* events
to the frontend — making the assertion non-genuine.

Strict-only behavior now: the assertion requires one of the four
stable role/testid markers (reasoning-block / reasoning-content /
reasoning-default / [data-message-role="reasoning"]) to render
within the timeout. Integrations that rely on CopilotKit's default
reasoning slot without a stable testid will fail this probe — by
design.

Replace REASONING_KEYWORDS export with REASONING_SELECTORS, drop
the readAssistantTranscript helper (no longer needed), and update
the test to exercise the testid-presence success path and the
timeout failure path.
2026-05-07 20:54:39 +02:00
Alem Tuzlak 82bd7857e6 fix(showcase/harness): tighten frontend-tools gradient hints
PILL_GRADIENT_HINTS contained naked hex prefixes (e.g. "#0", "#1",
"#2", "#ff", "#fb", "#3"-"#9") that accidentally cross-matched
across families — cosmic's #1e3a8a matches forest's "#1", so a
regression returning the same gradient for every pill could have
silently passed.

Drop ALL naked-hex prefix hints. Keep only color-name keywords:
  - sunset: sunset, orange, red, rose, pink, amber, coral, peach
  - forest: forest, green, emerald, lime, olive, teal
  - cosmic: cosmic, space, navy, magenta, purple, violet, indigo,
            fuchsia
If full-hex matching is ever needed, use full 6-digit codes from
the actual fixture rather than 1-2 character prefixes.

Also fix the broken "background did not change off baseline" test:
the previous assertion built a baseline-only page mock and then
discarded it via `void page`. Replaced with a fake-timer-driven
invocation that actually triggers the timeout branch, plus a
separate test for the wrong-family path.
2026-05-07 20:54:24 +02:00
Alem Tuzlak 9039716f65 fix(showcase/harness): correct agent-config knob delta math
The length-mode (responseLength) assertion compared the value-B
delta against the FULL CUMULATIVE pre-A transcript, so on the third
knob pair the math was strongly negative and the threshold check
always failed. Same shape mismatch existed on text-mode.

Thread a per-knob snapshot through each pair so both sides of the
comparison are per-turn deltas:
  - Capture priorCumulative BEFORE turn A (chained from the previous
    pair's value-B assertion via chainPriorCapture; pair 0 starts
    with empty since the page is blank pre-run).
  - Snapshot assertion stores aOnly = postA - priorCumulative and
    postACumulative for the next subtraction.
  - Diff assertion isolates onlyB = postB - postACumulative and
    compares aOnly vs onlyB (text equality or length delta).

Tests updated to construct KnobSnapshot bundles via a snapshotFor
helper and exercise the cumulative-prior third-pair scenario for
length-mode.
2026-05-07 20:54:04 +02:00
Alem Tuzlak ad228453b4 fix(showcase/harness): align tool-rendering catchall probes with production testids
The default-catchall probe queried `tool-status-{pending,executing,complete}`
but `DefaultToolCallRenderer` emits a single `copilot-tool-render-status`
pill with the lifecycle state on the container's `data-status` attribute.
Collapse the testid array to that single id and update the selector.

The custom-catchall probe queried `custom-catchall-render`, but the
production `CustomCatchallRenderer` emits `custom-wildcard-card`. Update
the constant + selector. Also swap the unmatched `"AAPL stock price"`
prompt for `"What's the current price of AAPL?"`, the verbatim key
already wired in `showcase/aimock/d5-all.json`, so the fixture
deterministically resolves instead of falling through to the live model.

Tests in both .test.ts files updated to match the new constants and
prompt; both files green locally.
2026-05-07 20:50:18 +02:00
Alem Tuzlak 6ad6dfbbd1 Merge slot B8 into blitz/lgp-genuine-pass/integration
Resolved conflicts:
- d5-all.json: appended B8's 26 new fixture entries to the existing 131
- gen-ui-open.json: kept ours (B3's verbatim pill messages match the suggestions.ts pills); dropped B8's paraphrased fixtures
- d5-gen-ui-open.ts: kept B7's basic-only routing + B8's iframe-presence assertion logic; updated PILL_PROMPT_PREFIX to '3D axis visualization (model airplane)' so it matches the actual pill message
2026-05-07 18:22:52 +02:00
Alem Tuzlak 2b085f22d2 Merge slot B7 into blitz/lgp-genuine-pass/integration 2026-05-07 18:20:29 +02:00
Alem Tuzlak 4097b166eb Merge slot B4 into blitz/lgp-genuine-pass/integration 2026-05-07 18:18:22 +02:00
Alem Tuzlak 83fd617f94 feat(showcase/harness): rewrite d5 smoke probes to genuine assertions
Rewrites 10 D5 smoke probes from keyword-match-on-transcript checks
to side-effect / DOM-mount / network-payload assertions per
Phase-2B of .claude/specs/lgp-test-genuine-pass.md:

- d5-frontend-tools: 3 per-pill turns (Sunset/Forest/Cosmic) asserting
  the page background data-attribute mutated to the pill's family-
  specific gradient and differs from the prior pill's gradient.
- d5-frontend-tools-async: notes-card testid mount + settled-state
  detection (list items OR empty-state copy).
- d5-agent-config: 3 knob pairs (tone/expertise/responseLength) sent
  as 6 turns; assertion compares value-A vs value-B response text or
  character-count delta. Aimock fixtures key on prompt-encoded knob
  tokens since JSON fixtures don't expose context-keyed predicates.
- d5-gen-ui-agent: 3 per-pill turns asserting agent-state-card mounts
  with ≥ 2 agent-step rows whose text fingerprint differs across
  pills (catches a regression where fixtures stop differentiating).
- d5-gen-ui-declarative: 4 per-pill turns asserting at least one
  catalog testid (declarative-card / metric / status-badge /
  pie-chart / bar-chart) mounted.
- d5-gen-ui-a2ui-fixed: pill prompt + a2ui-fixed-card testid mount
  assertion.
- d5-gen-ui-interrupt: 2 per-pill turns; click first time-picker-slot
  after time-picker-card mounts; assert time-picker-picked appears.
- d5-gen-ui-open: pill prompt; iframe[srcdoc] mounts with srcdoc
  length ≥ 100 chars.
- d5-shared-state-streaming: 3 per-pill turns asserting
  document-content text length ≥ STREAMING_MIN_FINAL_CHARS.
- d5-readonly-state-context: seeds [data-testid=ctx-name] with a
  sentinel; installs page.route() interceptor on /api/copilotkit;
  asserts the captured outgoing request body contains the sentinel.

Adds _genuine-shared.ts with the structural Page extension
(GenuinePage), pill-click helper, testid waiter, and runtime guard
that mirrors the _beautiful-chat-shared.ts pattern.

All 10 *.test.ts files updated with focused assertions over fakes —
none rely on the production page being live, all 63 unit tests pass.
2026-05-07 18:15:02 +02:00
Alem Tuzlak 7b1f43fd9f chore(showcase/aimock): add d5 probe fixtures keyed on context values
Adds per-pill aimock fixtures backing the Phase-2B genuine D5 probes:

- agent-config: 6 fixtures (3 knob pairs) so concise vs detailed
  responses differ deterministically; satisfies the new probe's
  text-diff + length-diff assertions.
- frontend-tools: 3 per-pill fixtures (sunset/forest/cosmic) emitting
  change_background tool calls with family-specific gradient hexes.
- frontend-tools-async: query_notes tool call so the NotesCard mounts.
- gen-ui-agent: 3 per-pill set_steps tool calls with distinct step
  content so the per-pill content-fingerprint assertion catches
  fixture-drift.
- gen-ui-declarative: 4 per-pill generate_a2ui calls + render_a2ui
  fixture for the secondary LLM call that paints the catalog.
- gen-ui-a2ui-fixed: SFO/JFK display_flight tool call.
- gen-ui-interrupt: 2 per-pill schedule_meeting tool calls with
  distinct time-slot payloads.
- gen-ui-open: generateSandboxedUi tool call with a non-trivial
  HTML payload so the iframe[srcdoc]-mount assertion has ≥ 100 chars
  to observe.
- shared-state-streaming: 3 per-pill write_document tool calls with
  substantive content payloads (≥ 100 chars each).
- readonly-state-context: pill-prompt fixture; the probe's network-
  payload assertion checks the request body, not the response.

d5-all.json is updated with the new entries prepended so first-match
precedence routes specific pill prompts to their per-pill fixtures
ahead of the generic adjacent matches.
2026-05-07 18:14:35 +02:00
Alem Tuzlak e67fcfbf84 feat(showcase/harness): add d5-gen-ui-open-advanced probe
Phase-2A wrong-target split. The old d5-gen-ui-open probe claimed BOTH
open-gen-ui and open-gen-ui-advanced via the same gen-ui-open literal,
which meant a regression on the advanced demo's iframe sandbox would
never surface (the basic-tier assertion didn't read iframe state). The
advanced demo now owns its own probe asserting iframe presence as the
distinguishing signal:

- d5-gen-ui-open-advanced: drives /demos/open-gen-ui-advanced, asserts
  the [data-testid="gen-ui-open-advanced-iframe"] testid (with a
  generic iframe fallback).

d5-gen-ui-open: scoped to the basic /demos/open-gen-ui route only;
preNavigateRoute no longer branches on the demos[] context.

d5-feature-mapping.ts: open-gen-ui-advanced now points at the new
gen-ui-open-advanced literal; d5-registry.ts adds the literal.
2026-05-07 17:52:31 +02:00
Alem Tuzlak e6644338d5 feat(showcase/harness): add d5-tool-rendering-default-catchall and d5-tool-rendering-custom-catchall probes
Phase-2A wrong-target split. Both registry IDs previously pointed at
d5-tool-rendering, which asserts a per-tool WeatherCard — the wrong
signal for catchall renderers. Each catchall surface now owns a probe
that asserts its own testid contract:

- d5-tool-rendering-default-catchall: drives the built-in default
  catchall renderer, asserts [data-testid="copilot-tool-render"] +
  [data-tool-name="get_weather"] + a tool-status-* lifecycle pill.
- d5-tool-rendering-custom-catchall: drives the user-supplied wildcard
  renderer, sends 2 distinct prompts (get_weather + get_stock_price)
  and asserts BOTH render through the same [data-testid="custom-catchall-render"]
  testid (cross-tool signature snapshot — the wildcard truly catches
  all tools, not just one).

Mapping in d5-feature-mapping.ts now points each registry ID at its
own probe; d5-registry.ts adds the two new D5FeatureType literals.
2026-05-07 17:52:16 +02:00
Alem Tuzlak 390ab6fbbc test(showcase/langgraph-python): rewrite subagents to 5 deterministic 3-card tests
- Drop the 8 stale tests that asserted travel-planner shapes
  (Current Itinerary / supervisor-indicator / .bg-gray-50). They
  predated the supervisor + 3-subagent rewrite and could not catch
  the 3 production bugs.
- New suite (5 tests, 0 skipped):
    1) page loads with composer + 3 verbatim suggestion pills + 3
       subagent role indicators (testid-based).
    2-4) one test per pill (Write a blog post / Explain a topic /
       Summarize a topic). Each clicks the pill, waits for all
       three role-scoped subagent cards to reach data-status=
       "complete", then asserts each card's subagent-result is
       non-empty AND does not contain the showcase-assistant
       boilerplate fragments. Test 4 also serves as a regression
       gate on the delegations reducer fix — without it, that pill
       returns HTTP 400 and the cards never reach complete.
    5) clicks any pill, waits for terminal state, then asserts the
       critic-card count is exactly 1 and stays at 1 with status
       complete across a 5s dwell — catches any return of the
       supervisor -> critic loop.
- Aimock fixtures: 3 verbatim pill chains (cold exposure training,
  LLM tool calling, reusable rockets) added to both d5-all.json and
  the harness source d5/mcp-subagents.json. Each chain drives the
  full supervisor flow (turnIndex 0..3) plus three nested sub-agent
  fixtures so every Researcher/Writer/Critic card surfaces real
  prose instead of showcase boilerplate.
2026-05-07 17:52:02 +02:00
Alem Tuzlak 2b7d93a157 feat(showcase/harness): split d5-mcp-subagents into d5-mcp-apps and d5-subagents
Phase-2A wrong-target split. The old d5-mcp-subagents probe claimed both
mcp-apps and subagents feature types but routed both to /demos/subagents,
leaving mcp-apps wrong-targeted. Each feature now has its own probe:
- d5-mcp-apps: drives /demos/mcp-apps, asserts iframe presence via the
  [data-testid="mcp-app-iframe"] testid (with iframe[sandbox] fallback).
- d5-subagents: drives /demos/subagents, asserts the 3 Phase-1D
  subagent-card testids (researcher, writer, critic), enforces the
  one-critic invariant, and rejects boilerplate placeholder text.

d5-feature-mapping.ts: comment block updated to reflect the split.
d5-registry.ts: no new literals needed (mcp-apps + subagents already
existed).
2026-05-07 17:51:36 +02:00
Alem Tuzlak 72342e1543 chore(showcase/aimock): add open-gen-ui pill fixtures with priority over showcase-assistant catch-all
The 5 of 7 open-gen-ui pill prompts (3D axis, neural network, quicksort,
calculator, ping-the-host) currently match the broad userMessage: 'hi'
catch-all in feature-parity.json (substring match on 'hi' inside 'Say hi
to the host', 'this', etc.) and resolve to the showcase-assistant
boilerplate greeting instead of emitting the generateSandboxedUi tool
call. That kills the iframe path on 5/7 of the pills.

Add 7 high-priority verbatim-prompt fixtures in d5-all.json (and the
source-of-truth showcase/harness/fixtures/d5/gen-ui-open.json) that emit
deterministic generateSandboxedUi tool calls with inline HTML + CSS in
the tool result. d5-all.json loads BEFORE feature-parity.json so the
first-match-wins ordering naturally beats the catch-all. Pill fixtures
appear at the top of the file so they also win against intra-bundle
specifics. Inline HTML is intentionally minimal — the assertion bar is
iframe presence + non-empty srcdoc, not iframe DOM introspection
(cross-origin blocked under sandbox=allow-scripts only).
2026-05-07 17:50:53 +02:00
Alem Tuzlak 265c1567bf chore(showcase/langgraph-python): Phase 0 cleanup of orphans and dead probes
- delete orphan e2e specs (hitl, interrupt-headless,
  tool-rendering-reasoning-chain)
- rename reasoning-default-render → reasoning-default (route + describe)
- delete dead D5 probes (hitl-steps, interrupt-headless,
  tool-rendering-reasoning-chain)
- update d5-feature-mapping: rename agentic-chat-reasoning →
  reasoning-custom and reasoning-default-render → reasoning-default;
  repoint hitl + hitl-in-chat-booking to hitl-text-input (still used by
  ag2/agno/built-in-agent)
- update d5-registry: drop hitl-steps, interrupt-headless,
  tool-rendering-reasoning-chain feature types
- update d5-reasoning-display preNavigateRoute branch logic and tests
- update probes.test.ts and e2e-deep.test.ts to use surviving feature
  types
2026-05-07 17:18:21 +02:00
Alem Tuzlak fd6f7d418e Merge remote-tracking branch 'origin/main' into fix/showcase-d5-beautiful-chat-followup
# Conflicts:
#	showcase/aimock/d5-all.json
2026-05-06 18:50:39 +02:00
Alem Tuzlak ff4d7efa2a refactor(showcase): split beautiful-chat D5 probe into per-pill family
The aggregated multi-turn probe from #4672 hit a CopilotKit v2 quirk on
/demos/beautiful-chat: only the FIRST useComponent tool call in a
conversation paints its component. Subsequent tool calls emit (the
agent's followup content arrives) but the component never mounts.
Reproduced cleanly without any frontend tool involvement —
pie-chart turn 1 paints 5 svg circles in seconds, bar-chart turn 2
emits "Bar chart rendered above..." but paints zero recharts elements.

The runner can't sidestep this from inside one conversation without a
page.reload() between turns, which the structural Page type doesn't
expose. Splitting into per-pill scripts means each probe gets its own
browser launch — fresh page state, fresh conversation, no useComponent
ordering pollution. CATALOG_TO_D5_KEY maps `beautiful-chat` to all
listed literals; isD5Green requires every key green for the cell to
advance to D5, and per-pill failure isolation surfaces in PB row names.

Coverage in this PR (5 pills):
  - beautiful-chat-toggle-theme   (frontend tool, html.dark flip)
  - beautiful-chat-pie-chart      (controlled gen-UI useComponent)
  - beautiful-chat-bar-chart      (controlled gen-UI useComponent)
  - beautiful-chat-search-flights (A2UI fixed-schema FlightCards)
  - beautiful-chat-schedule-meeting (HITL with slot-click resolution)

All 5 verified locally end-to-end (5/5 pass against the local stack).

Out of scope, intentionally (track in follow-up):
  - Excalidraw     — depends on mcp.excalidraw.com reachability
  - Calculator     — sandboxed iframe; dup of d5-gen-ui-open
  - Sales Dashboard — generate_a2ui → render_a2ui chain renders
                      Metric labels but Row-bound charts don't paint
                      recharts containers under aimock fixtures (live
                      pill against same fixture chain shows the
                      inverse symptom). Suggests aimock's
                      non-progressive arg streaming differs from a
                      live LLM in a way the A2UI binder is sensitive
                      to. Needs separate aimock/binder investigation.
  - Task Manager   — manage_todos dispatches and agent emits closing
                      content, but StateStreamingMiddleware's
                      state.todos propagation doesn't populate the
                      App pane TodoList through aimock — same suspected
                      root cause as Sales Dashboard.

Architecture details:
  - _beautiful-chat-shared.ts factors DOM helpers + per-pill
    assertions, mirroring _hitl-shared.ts's pattern for an extended
    Page type with click() + a runtime guard
  - Each fixture uses unique D5-prefixed userMessage substrings; the
    multi-stage Schedule Meeting flow uses hasToolResult false→true
    for round disambiguation (no toolCallId leakage since each probe
    runs in its own fresh page session)
2026-05-06 18:40:47 +02:00
Alem Tuzlak 48cd3b2c93 feat(showcase/voice): D5 mapping + sample-button bypasses /transcribe (#4674)
## Summary

- **Dashboard mapping fix.** `CATALOG_TO_D5_KEY` in
`showcase/shell-dashboard/src/lib/live-status.ts` was missing `voice →
["voice"]`, so `computeMaxPossible` capped the langgraph-python voice
cell at D4 even when the d5-voice probe row was green. The harness
`REGISTRY_TO_D5` already had the entry; only the dashboard mirror was
out of sync.
- **Sample-button decoupled from `/transcribe`.** The "Play sample"
button used to fetch `sample.wav` and POST it to the runtime's
transcription endpoint, which made the sample button and the mic
indistinguishable under aimock (both returned the canned transcription).
Reworked it into a synchronous static-text injector — sample button is
now a deterministic test/demo affordance, and the mic is the only path
that exercises real Whisper transcription. Synced across all 18
voice-enabled integrations. Phrase stays `"What is the weather in
Tokyo?"` so aimock's `weather in Tokyo` substring fixture still matches.
- **Probe-test parity.** Added the missing `d5-voice.test.ts` companion
(every other `d5-*.ts` script has one) — 9 tests covering registration,
`buildTurns`, `preFill` (sample-button click + textarea-poll path), and
the weather/Tokyo assertion.
- **QA + e2e cleanup** for langgraph-python: dropped the
no-longer-applicable "Transcribing…" mid-flight assertion and the `block
/demo-audio/sample.wav` error-state subsection. Other 16 integrations'
qa/e2e files follow in a parity sync PR.

## Test plan

- [x] `nx test @copilotkit/showcase-harness -- --run d5-voice` → 9/9
pass
- [x] `npm test` in `showcase/shell-dashboard` → 509/510 pass (1
skipped, 0 failed)
- [x] `nx build @copilotkit/showcase-harness` → clean
- [x] Local boot: `langgraph-cli dev` (port 8123) + `next dev` (port
3000) + dashboard (port 3002) — voice page at `/demos/voice` renders,
"Play sample" injects the canned phrase instantly, send → agent returns
weather, mic → real Whisper transcription with `OPENAI_API_KEY` set
- [ ] Reviewer: confirm the langgraph-python voice cell on the live
dashboard advances to D5 once the next d5-voice probe tick lands a green
row
2026-05-06 18:25:33 +02:00
github-actions[bot] 9e93f951e5 style: auto-fix formatting 2026-05-06 16:15:53 +00:00
Alem Tuzlak 728ed61ce8 feat(showcase/voice): D5 mapping + sample-button bypasses /transcribe
The langgraph-python voice cell sat at D4 even when its d5-voice probe
row was green. Root cause: the dashboard's CATALOG_TO_D5_KEY mirror in
showcase/shell-dashboard/src/lib/live-status.ts was missing voice ->
["voice"], so computeMaxPossible capped voice at D4 regardless of probe
state. The harness REGISTRY_TO_D5 already had the entry; only the
dashboard mirror was out of sync.

Separately, the "Play sample" button used to fetch sample.wav and POST
it to /transcribe. With aimock that meant both the sample button AND
the mic returned the same canned response, which made it impossible to
demo the mic path locally without conflating the two affordances.
Reworked the button into a synchronous static-text injector
(onTranscribed(sampleText)) so:

- Sample button = deterministic test/demo affordance, no runtime calls.
- Mic = real Whisper transcription via /transcribe.

Synced across all 18 voice-enabled integrations. Phrase stays "What is
the weather in Tokyo?" so aimock's "weather in Tokyo" substring fixture
still matches.

Also adds the missing d5-voice.test.ts companion (every other d5-* probe
script has one) and trims the langgraph-python qa/voice.md + e2e steps
that depended on the now-removed async behavior.
2026-05-06 18:11:24 +02:00
Alem Tuzlak 53d7999c05 Merge remote-tracking branch 'origin/main' into feat/showcase-d5-headless-chat
# Conflicts:
#	showcase/aimock/d5-all.json
2026-05-06 17:04:00 +02:00
Alem Tuzlak 5034e83054 fix(showcase/harness): use PB-validator-compliant email for local superuser
PocketBase 0.22's email validator rejects `admin@localhost` (single-
label TLD) with `validation_is_email: Must be a valid email address`,
breaking PB auth from the harness CLI on a fresh local stack —
`pb-write-failed` / `pb-auth failed: 400` on every --live run.

Switch the hardcoded local creds to `admin@localhost.dev`. The
matching superuser must be created with the running server's data
directory pinned: `pocketbase admin create admin@localhost.dev
showcase-local-dev --dir=/pb_data` (the create command's default
`./pb_data` differs from the server's `--dir=/pb_data`, so the record
otherwise lands in a different SQLite file and the running server
can't see it).
2026-05-06 16:25:53 +02:00
Alem Tuzlak e5c68c02ee fix(showcase/harness): align CLI smoke input slug shape with discovery
`buildSmokeInputs` was passing `name: manifest.name` (the display name,
e.g. "LangGraph (Python)") which the liveness driver's `deriveSlug`
stripped of a leading `showcase-` prefix and used as the slug —
producing rows like `health:LangGraph (Python)` and
`agent:LangGraph (Python)` that didn't join with anything else on the
dashboard's depth ladder.

Production discovery sets `name` to the Railway service name
(`showcase-<slug>`), which strips cleanly to the slug. Mirror that
shape from the local CLI so all rows for a service share the same
keyspace and the dashboard cell can advance past D0.
2026-05-06 16:25:29 +02:00
Alem Tuzlak 85afa8ee9e fix(showcase/harness): wire pbWriter into ProbeContext for live writes
The CLI's runner.ts created a `pbWriter` when --live was passed but
never plumbed it into `ctx.writer`, so every per-feature side row from
every driver (e2e-deep's `d5:<slug>/<featureType>`, liveness's
`health:<slug>` / `agent:<slug>`, etc.) was silently dropped. Only
the aggregate primary results made it to PocketBase, which is why the
dashboard matrix showed every cell as D0 even after a successful
--live probe run.

Now `ctx.writer = pbWriter` when --live is on; without --live the
field is omitted, matching the legacy "no side emission" behavior.
2026-05-06 16:24:30 +02:00
Alem Tuzlak 3eb53a8621 feat(showcase): D5 probe for headless-complete + extend headless-simple (langgraph-python)
Promotes /demos/headless-complete to its own D5 feature type so the
dashboard cell can reach D5 instead of riding on the headless-simple
probe (which was navigating to /demos/headless-simple regardless of
which catalog feature triggered it).

- New gen-ui-headless-complete D5 feature type + script that clicks
  each suggestion chip via preFill and asserts the right surface
  renders: WeatherCard (get_weather), StockCard (get_stock_price),
  HighlightNote (frontend useComponent), Excalidraw best-effort, and
  the canonical "Asia is the largest continent" text reply.
- Existing gen-ui-headless script now drives both turns by chip
  click (Profile card + Largest continent) instead of typing.
- Fixtures pin narration legs with both userMessage AND toolCallId
  and order them before the bare userMessage toolCall fixture —
  aimock's toolCallId matcher reads the LAST tool message in the
  request, but in a multi-turn probe that "last tool" stays on a
  previous turn's id until a new tool runs, which would otherwise
  hijack a later turn's prompt with a stale narration.
- headless-complete UserBubble + AssistantBubble now carry
  data-message-role so the harness conversation runner can detect
  message arrivals (mirrors the headless-simple convention).
- Mappings updated in lockstep:
    - REGISTRY_TO_D5:  headless-complete -> ["gen-ui-headless-complete"]
    - CATALOG_TO_D5_KEY (dashboard): same.
2026-05-06 16:21:43 +02:00
Alem Tuzlak 653abdb901 feat(showcase): expand beautiful-chat D5 probe to 7 of 9 pills
Builds on the initial Search Flights-only probe by extending to a
multi-turn ConversationTurn[] that asserts surface-specific render
fingerprints for 7 of the 9 pills in /demos/beautiful-chat.

Pills covered (turn order is load-bearing — see script jsdoc):
  1. Toggle Theme        — html.dark class flip (toggleTheme frontend tool)
  2. Pie Chart           — >= 3 svg circles (pieChart useComponent)
  3. Bar Chart           — recharts container + >= 2 bar rectangles
  4. Search Flights      — A2UI FlightCard literal fingerprints (#4668 path)
  5. Schedule Meeting    — MeetingTimePicker mounts; assertion CLICKS a slot
                           to resolve the HITL pause before subsequent turns
  6. Sales Dashboard     — A2UI dynamic generate_a2ui → secondary render_a2ui;
                           "Total Revenue" + recharts container (90s budget)
  7. Task Manager        — enableAppMode + manage_todos; "To Do" column +
                           canonical todo title visible. MUST be last
                           (flips layout, breaks chat input on narrow widths)

Pills intentionally skipped (track in follow-up):
  - Excalidraw Diagram   — depends on mcp.excalidraw.com reachability,
                           turning D5 reliability into a 3rd-party uptime bet
  - Calculator App       — sandboxed iframe makes assertions cross-frame-fragile;
                           generateSandboxedUi already covered by d5-gen-ui-open
                           on a different demo route (would duplicate coverage)

Probe uses the runner's structural ConversationPage type for most assertions
(via page.evaluate helpers in the d5-chat-css globalThis-cast pattern, which
keeps the harness's Node-only tsconfig clean of DOM lib types). Schedule
Meeting narrows to an extended Page type with click() — same runtime-guarded
cast used in d5-hitl-text-input — so it can dispatch the HITL slot click.

Fixture file expanded from 2 to 15 entries covering all 7 turns plus their
multi-stage chains (Sales Dashboard primary + secondary LLM + post-tool;
Task Manager enableAppMode → manage_todos → narration). Re-bundled into
showcase/aimock/d5-all.json (additions only — no other fixtures changed).

Conversation runner reports failure_turn so per-pill failure isolation is
preserved on the dashboard's drilldown without needing 7 separate D5 literals.
2026-05-06 15:14:09 +02:00
Alem Tuzlak e11c1d0810 feat(showcase): D5 conversation probe for beautiful-chat (langgraph-python)
Beautiful Chat was capped at D4 in the dashboard because it had no
dedicated D5 probe and was deliberately excluded from CATALOG_TO_D5_KEY
(commit 974494ecb stripped the freeloading "agentic-chat" alias). PR
#4668 fixed the A2UI surface rendering and added e2e tests, but those
land at the D3 tier — D5 is a separate probe with its own driver.

Changes:

- New d5-beautiful-chat probe asserts the A2UI fixed-schema FlightCard
  surface renders with literal United/Delta/$349/$289 fingerprints from
  the search_flights tool. 60s budget on first card, 5s on siblings.
- New harness/fixtures/d5/beautiful-chat.json with two-stage fixture
  (hasToolResult false→true) mirroring the gen-ui-headless pattern.
  Fixture spliced into the bundled aimock/d5-all.json.
- New "beautiful-chat" D5FeatureType literal in the registry's union +
  runtime mirror.
- d5-feature-mapping.ts: replace "beautiful-chat": ["agentic-chat"]
  alias with ["beautiful-chat"] so the probe targets its own dedicated
  PB key instead of freeloading agentic-chat's green status.
- live-status.ts CATALOG_TO_D5_KEY: re-add "beautiful-chat":
  ["beautiful-chat"] so computeMaxPossible lifts the D4 cap to D5.
2026-05-06 14:35:07 +02:00
Tyler Slaton 04f77586f3 style: fix formatting failures on main
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-04 13:46:32 -07:00
Jordan Ritter e7391e2c88 feat(showcase): skip D5 probes for recently deployed services (#4571)
## Summary

- When a Railway service deployed within the last 2 minutes, the D5
probe driver skips all features with green side rows instead of
launching a browser and producing false reds from deploy churn
- Discovery source now threads `deployedAt` (from
`latestDeployment.createdAt`) through to drivers via
`RailwayServiceInfo`
- Skip fires before script loader and browser launch so recently
deployed services cost zero probe resources

## Changes

**`showcase/harness/src/probes/discovery/railway-services.ts`**
- Added `deployedAt: string` to `RailwayServiceInfo` interface
- Added `createdAt` to the Zod schema for `latestDeployment` and the
GraphQL query
- Extracts `createdAt` in the enrichment loop and passes it through

**`showcase/harness/src/probes/drivers/e2e-deep.ts`**
- Added `DEPLOY_CHURN_GRACE_MS` constant (120,000ms = 2 minutes)
- Added `deployedAt` to the driver's input Zod schema
- Deploy-churn skip logic inserted after feature resolution, before
script loading and browser launch
- When `deployedAt` is within the grace window: emits green side rows
with `note: "skipped: deploy in progress (Ns ago)"` and returns
aggregate green with all features in `skipped[]`

**`showcase/harness/src/probes/drivers/e2e-deep.test.ts`**
- 8 new tests covering: skip path, normal execution when outside grace
window, backwards compat (absent/empty/unparseable `deployedAt`),
boundary at exactly `DEPLOY_CHURN_GRACE_MS`, and 0s-age edge case

## Test plan

- [x] `npx tsc --noEmit -p showcase/harness/tsconfig.json` passes
cleanly
- [x] All 40 e2e-deep driver tests pass (8 new + 32 existing)
- [x] All 58 railway-services discovery tests pass unchanged
- [ ] CI green
2026-05-01 10:42:08 -07:00
Alem Tuzlak 86acef1a1b fix(showcase): loosen reasoning-display probe to OR(testid, keyword) (#4580)
## Summary

PR #4579 tightened the d5-reasoning-display probe to require BOTH a
reasoning-role testid AND a keyword in the transcript. That flipped most
integrations from D5 to D4 because the strong selector check is only
valid for cells whose AG-UI bridge emits role-reasoning messages with a
stable testid. Cells that surface reasoning inline as assistant text —
`llamaindex`, `crewai-crews`, several variants that use CopilotKit's
default `CopilotChatReasoningMessage` slot which has no `data-testid` —
were always green via the keyword check and shouldn't have been
regressed.

This PR loosens the probe to pass on **either** signal:

- A known reasoning testid (`reasoning-block`, `reasoning-content`,
`reasoning-default`) or `[data-message-role="reasoning"]` — strong
signal that AG-UI REASONING_MESSAGE_* events reached the frontend.
- OR a reasoning keyword in the assistant transcript — looser fallback
for cells that surface reasoning inline as text, or use the default
reasoning slot.

It also broadens the testid list to cover `reasoning-content` and
`reasoning-default`, which are used in several showcase ReasoningBlock
components but were missing from the original tightening.

The strict path is preserved where it works: `langgraph-python` and
`langgraph-fastapi` still emit role-reasoning messages with
`data-testid="reasoning-block"` and pass the strong check first. The
strict end-to-end assertions live in `langgraph-python`'s e2e specs from
PR #4579 and continue to enforce it for that integration specifically.

`claude-sdk-typescript`'s "assistant did not respond within 30000ms"
failure is a separate backend-timeout issue, not addressed here.

## Notes

Committed with `--no-verify` (worktree has no node_modules, lefthook
can't run nx; CI runs the same checks).

## Test plan

- [ ] CI fixture-validation passes
- [ ] `showcase test crewai-crews --d5 --verbose` — reasoning-display
green again
- [ ] `showcase test pydantic-ai --d5 --verbose` — reasoning-display
green again
- [ ] `showcase test langgraph-python --d5 --verbose` — still green via
strict selector path
- [ ] Production D4 → D5 reverts on most integrations after deploy
2026-05-01 14:37:28 +02:00
Alem Tuzlak a3586fb62a fix(showcase): revert reasoning field on aimock d5 reasoning fixture
PR #4579 added a `reasoning` field to the "show your reasoning step by
step" fixture so aimock would emit response.reasoning_summary_* deltas
for the OpenAI Responses API path. Side effect: aimock's Chat
Completions handler also emits non-standard `reasoning_content` deltas
(DeepSeek/Qwen-style) ahead of the role/content chunks. Many
integrations' OpenAI client adapters don't expect those deltas and
either hang or fail to parse the stream — manifesting as "assistant
did not respond within 30000ms" across most reasoning cells in
production.

Restore the original content-only fixture. The langgraph-python /
langgraph-fastapi agent fixes from #4579 still work against real
OpenAI (gpt-5-mini + Responses API streams real reasoning summaries),
but the aimock-driven path no longer exercises the role-reasoning
render — keyword-only assertion in the d5 probe handles that.
2026-05-01 14:07:21 +02:00
Alem Tuzlak 2e18d07d5e fix(showcase): loosen reasoning-display probe to OR(testid, keyword)
Tightening the probe to require BOTH a reasoning-role testid AND a
keyword match flipped most integrations from D5 to D4 in production.
The strong selector check is only valid for cells whose AG-UI bridge
emits role-reasoning messages with a stable testid; cells that surface
reasoning inline as assistant text (llamaindex, crewai-crews, several
default-slot variants whose CopilotChatReasoningMessage has no testid)
were always green via the keyword check and shouldn't have been
regressed.

Pass on either signal: a known reasoning testid OR a reasoning keyword
in the transcript. Also broaden the testid list to cover the
reasoning-content and reasoning-default variants used in showcase.

Doesn't change the langgraph-python / langgraph-fastapi outcome — those
cells still emit role-reasoning messages with data-testid="reasoning-block"
and pass the strong check first. The integration-specific assertions in
langgraph-python's e2e specs continue to enforce the strict path.
2026-05-01 14:02:13 +02:00
github-actions[bot] 6032b374c4 style: auto-fix formatting 2026-05-01 11:32:58 +00:00
Alem Tuzlak dca1b9894d fix(showcase): emit reasoning events in langgraph-python and langgraph-fastapi
The agentic-chat-reasoning and reasoning-default-render cells in
langgraph-python and langgraph-fastapi were configured with
gpt-4o-mini + use_responses_api=False, which never produces AG-UI
REASONING_MESSAGE_* events: gpt-4o-mini is not a reasoning model and
the Chat Completions API does not surface reasoning summary items at
all. The frontend's reasoningMessage slot was rendering nothing,
even though the cells were billed as "reasoning" demos.

- Switch both reasoning agents to gpt-5-mini (override via
  OPENAI_REASONING_MODEL) routed through the Responses API with
  reasoning={"effort":"medium","summary":"detailed"} so the model's
  chain of thought streams as content blocks that @ag-ui/langgraph
  translates into REASONING_MESSAGE_* events.
- Update the aimock d5-all.json and harness reasoning-display.json
  fixtures to include a "reasoning" field so aimock emits
  response.reasoning_summary_text.delta SSE events deterministically
  in CI without hitting a real LLM.
- Add a "Show reasoning" useConfigureSuggestions pill on both
  reasoning demo pages so the user can trigger the fixture-matched
  prompt with one click.
- Tighten the d5-reasoning-display probe: it now also asserts a
  reasoning-role message rendered via [data-testid="reasoning-block"]
  or [data-message-role="reasoning"], so a plain text response
  containing the word "reasoning" no longer falsely passes.
- Un-skip the three streaming reasoning-block tests in
  langgraph-python's agentic-chat-reasoning.spec.ts and add a
  suggestion-pill test; expand the reasoning-default-render spec to
  cover the default reasoning slot.
- Update the langgraph-python QA doc to describe the new model +
  Responses API setup and the suggestion-pill flow.
2026-05-01 13:30:28 +02:00