From 454cc6c3cd778add267b8d6a4151b07add8316d5 Mon Sep 17 00:00:00 2001 From: Paul Lizer Date: Fri, 9 Oct 2026 15:24:59 -0400 Subject: [PATCH] Remove duplicate agent and tabular progress panels in V2 Keep activity details in the reasoning dropdown and preserve background-export controls. Add lane and browser regression coverage, update documentation, and bump to 0.261.318. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- application/single_app/config.py | 2 +- application/v2_ui/src/lib/activityLanes.ts | 6 +- .../features/V2_TABULAR_ANALYSIS.md | 35 +++-- ...ENT_TABULAR_DUPLICATE_PROGRESS_CARD_FIX.md | 87 ++++++++++++ .../test_v2_agent_tabular_progress_cards.mjs | 83 +++++++++++ .../test_v2_orchestration_progress_lane.mjs | 13 +- .../test_v2_orchestration_streaming_bubble.py | 129 +++++++++++++++--- 7 files changed, 315 insertions(+), 40 deletions(-) create mode 100644 docs/explanation/fixes/V2_AGENT_TABULAR_DUPLICATE_PROGRESS_CARD_FIX.md create mode 100644 functional_tests/test_v2_agent_tabular_progress_cards.mjs diff --git a/application/single_app/config.py b/application/single_app/config.py index 3ba6aef1f..463e5952d 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -101,7 +101,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.261.317" +VERSION = "0.261.318" IS_DEVELOPMENT = is_development_env_enabled() # Opt-out for deployments where App Service Easy Auth is active but the platform diff --git a/application/v2_ui/src/lib/activityLanes.ts b/application/v2_ui/src/lib/activityLanes.ts index ee102b74c..fd738433d 100644 --- a/application/v2_ui/src/lib/activityLanes.ts +++ b/application/v2_ui/src/lib/activityLanes.ts @@ -77,7 +77,8 @@ const LANE_RULES: LaneRule[] = [ { key: 'tabular', title: 'Tabular analysis', - showsCard: true, + // Tool details stay under the reasoning toggle; durable export status has its own card. + showsCard: false, currentStepPrefix: 'Current tabular step', initialStatus: 'Gathering workbook evidence', completedStatus: 'Workbook evidence ready', @@ -133,7 +134,8 @@ const LANE_RULES: LaneRule[] = [ { key: 'agent', title: 'Agent progress', - showsCard: true, + // The reasoning toggle already exposes the agent's activity without a duplicate summary. + showsCard: false, currentStepPrefix: 'Current tool', initialStatus: 'Connecting to the selected agent', completedStatus: 'Response ready', diff --git a/docs/explanation/features/V2_TABULAR_ANALYSIS.md b/docs/explanation/features/V2_TABULAR_ANALYSIS.md index 156538f1d..00252c506 100644 --- a/docs/explanation/features/V2_TABULAR_ANALYSIS.md +++ b/docs/explanation/features/V2_TABULAR_ANALYSIS.md @@ -15,6 +15,10 @@ to it, and the reply's own text asserted otherwise, so the failure was invisible This change closes that gap and five smaller ones around it. Implemented in version **0.261.057**. +Thought-summary progress cards were removed in version **0.261.318**, tracked in +`application/single_app/config.py`. Activity details remain in the reasoning dropdown; +background-export status and its run controls remain available on the generated-file card. + ### Dependencies None added. Every route and payload this uses already existed and is unchanged; the work is @@ -163,9 +167,14 @@ payloads already describe both. A tabular turn opens with the agent hand-off sen more specific lane supersedes the general agent one — otherwise a workbook run would be labelled "Agent progress". -A lane can claim steps without drawing a card. Orchestration's lane has `showsCard: false`. -Its entry still keeps planner and run steps out of the agent and tabular lanes, but the plan -card and the reasoning toggle already show that progress, and a third display repeated them. +A lane can claim steps without drawing a card. Since **0.261.318**, agent, tabular, and +orchestration lanes all use `showsCard: false`. Their classification remains intact, but +thought-summary headings, percentages, bars, and repeated status text no longer appear +above the reasoning dropdown or inside a finished answer's expanded reasoning. + +The dropdown still exposes the activity details when opened. Orchestration's plan card +reports its run status, and the separate generated-file card reports durable export +progress with Continue/Cancel controls. Neither operational surface is removed. Every activity is emitted at least twice, running then completed, keyed by `activity_key`. They are folded into a map so a tool call is counted once rather than once per frame. @@ -233,8 +242,9 @@ where it was saved, a preview and a download. offers Continue or Cancel where the server allows them. It replaces itself with the finished files. -**A run being analysed.** The reasoning panel shows a progress card above the steps while the -run is live, without needing to be expanded. +**A run being analysed.** Expand **N reasoning steps** to read the agent's tool activity or +workbook-analysis details. The dropdown starts collapsed, with no extra thought-summary +progress card. Details remain available on the finished response. **An uploaded spreadsheet.** Click its name in the thread to see it as a table, with **Download original** when the original file is still in storage. The name is inert in a @@ -251,9 +261,14 @@ states how many rows matched and shows the first few, with controls for 25 or al |---|---| | `functional_tests/test_v2_tabular_parity.py` | The client reads the metadata keys the server writes; every requested route is registered; the run-control routes carry their decorators; the confirmation thresholds survive sanitization; the thought frame fields are carried; every component is mounted; the confirmation precedes the send; run members are read from the key the server sends; a withheld file offers no download; the upload preview is gated on what the endpoint supports; no remote assets | | `functional_tests/test_v2_tabular_parity_logic.ts` | 70 behavioural checks: normalising and de-duplication, download target selection, the compact-layout rule, preview table construction, run progress and polling, artifact set completion and member ordering, approval states, lane detection and counting, the completion rule, the progress ratchet, the confirmation heuristic, row banding, CSV parsing | +| `functional_tests/test_v2_agent_tabular_progress_cards.mjs` | Agent and tabular markers still claim their lanes without cards during running, completed, and failed activity; agent-to-tabular precedence and thought content remain intact | +| `ui_tests/test_v2_orchestration_streaming_bubble.py` | Agent/tabular summary cards remain absent on desktop and mobile while mouse/keyboard access to live, finished, and remounted reasoning stays available; orchestration status is unchanged | -Both are run by executing the Python file, which bundles the TypeScript with esbuild and runs -it under node, skipping that half when `application/v2_ui/node_modules` is absent. +The tabular parity checks are run by executing the Python file, which bundles the TypeScript +with esbuild and runs it under node, skipping that half when +`application/v2_ui/node_modules` is absent. Run the focused card-policy checks with `node` +and the browser checks with `pytest`; their commands and current validation results are in +the [duplicate progress card fix](../fixes/V2_AGENT_TABULAR_DUPLICATE_PROGRESS_CARD_FIX.md). ### Known limitations @@ -269,9 +284,9 @@ it under node, skipping that half when `application/v2_ui/node_modules` is absen - **Non-boolean tabular admin settings are still not editable in V2.** That is the general "admin settings edits boolean capabilities only" limitation rather than anything specific to tabular, and is unchanged by this work. -- **Progress cards cover tabular and agent work only.** Orchestration has a lane so its steps - are claimed, but it draws no card, because the plan card shows a run's progress. Workflow - activity emits compatible payloads but is not yet wired end to end, so it has no lane entry. +- **Thought-summary progress cards are intentionally absent.** Agent and tabular activity + is read through the reasoning dropdown rather than an additional top-level card. This + does not remove the separate progress and recovery controls for a background export. ## See also diff --git a/docs/explanation/fixes/V2_AGENT_TABULAR_DUPLICATE_PROGRESS_CARD_FIX.md b/docs/explanation/fixes/V2_AGENT_TABULAR_DUPLICATE_PROGRESS_CARD_FIX.md new file mode 100644 index 000000000..81ae72aab --- /dev/null +++ b/docs/explanation/fixes/V2_AGENT_TABULAR_DUPLICATE_PROGRESS_CARD_FIX.md @@ -0,0 +1,87 @@ +# V2 Agent and Tabular Duplicate Progress Card Fix (v0.261.318) + +## Issue + +When a selected agent responded in V2, an **Agent progress** card appeared above the +collapsed reasoning dropdown. It repeated activity that was already available by expanding +the dropdown: the current tool, latest status sentence, counts, and a progress percentage. +Workbook analysis displayed a similar **Tabular analysis** card. These summaries distracted +from the response and made activity details visible even when the user had not opened them. + +Fixed in version: **0.261.318**, tracked by the `VERSION` update in +`application/single_app/config.py`. + +## Root cause + +`activityLanes.ts` classifies thought events as agent, tabular, or orchestration work. +Its agent and tabular entries set `showsCard: true`. The shared `ThoughtsProgressCard` +renderer therefore drew their summaries above live reasoning and inside a finished answer's +expanded reasoning. + +Orchestration already used `showsCard: false` to avoid a duplicate summary beside its plan +and reasoning details. Agent and tabular activity needed the same presentation policy, +without removing the events or changing how tools execute. + +## Technical details + +### Files modified + +- `application/v2_ui/src/lib/activityLanes.ts`: agent and tabular entries now set + `showsCard: false`. Lane matching, precedence, status counting, and completion handling + remain unchanged. +- `application/single_app/config.py`: application version incremented to `0.261.318`. +- `functional_tests/test_v2_agent_tabular_progress_cards.mjs`: exercises real lane + classification for hand-off text, step types, activity kinds, lane keys, and plugins. +- `functional_tests/test_v2_orchestration_progress_lane.mjs`: updates agent/tabular + expectations while retaining orchestration classification coverage. +- `ui_tests/test_v2_orchestration_streaming_bubble.py`: verifies hidden agent/tabular + summaries and usable reasoning details with production components and CSS. +- `docs/explanation/features/V2_TABULAR_ANALYSIS.md`: describes the current distinction + between reasoning activity and background-export status. + +The renderer already returns nothing when `showsCard` is false. No component rewrite, +CSS visibility workaround, new setting, route change, or dependency is required. + +### User-facing behavior + +Both thought-summary cards are removed completely, including their headings, badges, +percentages, progress bars, counters, current-tool summaries, and repeated status sentences. +Their underlying thought records remain available. + +The **N reasoning steps** dropdown keeps its existing label and starts collapsed. It still +opens with mouse or keyboard during generation and on finished responses. Tool failures +remain readable there; suppressing the summary does not discard their details. + +V1 is unchanged. V2's lightweight **Thinking**/**Planning** indicator, streamed answer, +connection notices, error handling, and orchestration plan/run status are also unchanged. + +The separate background-export card is intentionally preserved. `TabularRunStatus.tsx` +still reports durable file generation, polls the server, offers **Continue**/**Cancel** when +allowed, and yields to the finished downloads. That operational status is not the +thought-summary card above the reasoning dropdown. + +## Validation + +Before the fix, the new lane checks failed because `showsCard` was true, and the desktop-agent +browser case found one progress bar where none was expected. After the fix: + +| Check | Result | +|---|---| +| `node --test .\functional_tests\test_v2_orchestration_progress_lane.mjs .\functional_tests\test_v2_agent_tabular_progress_cards.mjs` | 20 passed | +| `npm --prefix .\application\v2_ui run build -- --outDir ..\..\ui_tests\artifacts\orchestration-plan-editor --logLevel error` | Passed, including TypeScript checking | +| `python -m pytest .\ui_tests\test_v2_orchestration_streaming_bubble.py -q` | 8 passed in local Chromium | +| `python .\functional_tests\test_v2_tabular_parity.py` | 13 of 14 checks passed, including all 70 TypeScript logic checks; existing confirmation-handler lookup failure | + +Browser coverage includes desktop and mobile agent/tabular turns, collapsed and expanded +reasoning, Enter/Space activation, failed activity, finished answers, and remounted saved +responses. It checks that the whole summary card is absent rather than hiding only its bar. +The existing orchestration planning/running cases also pass. + +The wider tabular parity script has an unrelated assertion, **Could not locate the composer +submit handler**. Re-running that check against the unchanged `HEAD` version of `Composer.tsx` +reproduces the same failure. Neither the composer nor that assertion is modified by this fix. + +## Related + +- [V2 Tabular Analysis](../features/V2_TABULAR_ANALYSIS.md) +- [Orchestration Duplicate Progress Card Fix](ORCHESTRATION_DUPLICATE_PROGRESS_CARD_FIX.md) diff --git a/functional_tests/test_v2_agent_tabular_progress_cards.mjs b/functional_tests/test_v2_agent_tabular_progress_cards.mjs new file mode 100644 index 000000000..6d684a799 --- /dev/null +++ b/functional_tests/test_v2_agent_tabular_progress_cards.mjs @@ -0,0 +1,83 @@ +// test_v2_agent_tabular_progress_cards.mjs +// Version: 0.261.318 +// Implemented in: 0.261.318 +// Agent and tabular activity stays classified and available as reasoning, but neither lane +// draws a duplicate progress card in live or saved V2 responses. + +import assert from 'node:assert/strict'; +import test from 'node:test'; +import './test_support/tsResolve.mjs'; + +// Register the repository resolver before loading TypeScript application modules. +const { buildLaneProgress } = await import('../application/v2_ui/src/lib/activityLanes.ts'); + +const markers = [ + ['agent hand-off text', 'agent', { content: 'Sending to agent researcher' }], + ['agent step type', 'agent', { step_type: 'agent_tool_call', content: 'Calling search.' }], + ['agent activity lane', 'agent', { + content: 'Calling search.', + activity: { lane_key: 'agent', activity_key: 'search', title: 'Search', status: 'running' }, + }], + ['tabular step type', 'tabular', { step_type: 'tabular_analysis', content: 'Reading a workbook.' }], + ['tabular tool kind', 'tabular', { + activity: { kind: 'tabular_tool_invocation', activity_key: 'read', status: 'running' }, + }], + ['tabular post-processing kind', 'tabular', { + activity: { kind: 'tabular_post_processing', activity_key: 'export', status: 'running' }, + }], + ['tabular activity lane', 'tabular', { activity: { lane_key: 'tabular', status: 'running' } }], + ['tabular plugin', 'tabular', { + activity: { plugin_name: 'TabularProcessingPlugin', status: 'running' }, + }], +]; + +for (const [name, lane, thought] of markers) { + test(`${name} claims its lane without drawing a live or saved card`, () => { + for (const live of [true, false]) { + const thoughts = [structuredClone(thought)]; + const original = structuredClone(thoughts); + const progress = buildLaneProgress(thoughts, { live }); + assert.ok(progress); + assert.equal(progress.lane.key, lane); + assert.equal(progress.lane.showsCard, false); + assert.deepEqual(thoughts, original, 'Reasoning content must not be removed or mutated.'); + } + }); +} + +for (const lane of ['agent', 'tabular']) { + for (const status of ['running', 'completed', 'failed']) { + test(`${lane} ${status} activity never brings back a progress card`, () => { + const thoughts = [ + { step_type: 'agent_tool_call', content: 'Sending to agent analyst' }, + { + step_type: lane === 'agent' ? 'agent_tool_call' : 'tabular_analysis', + content: status === 'failed' ? 'The tool could not finish.' : 'Reading evidence.', + activity: { + activity_key: 'read', + lane_key: lane, + kind: lane === 'tabular' ? 'tabular_tool_invocation' : 'agent_tool_invocation', + title: 'Read evidence', + status, + }, + }, + ]; + for (const live of [true, false]) { + const progress = buildLaneProgress(thoughts, { live }); + assert.ok(progress); + assert.equal(progress.lane.key, lane, 'Tabular must still supersede the agent hand-off.'); + assert.equal(progress.lane.showsCard, false); + assert.equal(progress.failedCount, status === 'failed' ? 1 : 0); + } + + const answered = buildLaneProgress([ + ...thoughts, + { step_type: 'generation', content: 'Agent responded with the answer.' }, + ]); + assert.ok(answered); + assert.equal(answered.lane.key, lane); + assert.equal(answered.completed, true); + assert.equal(answered.lane.showsCard, false); + }); + } +} diff --git a/functional_tests/test_v2_orchestration_progress_lane.mjs b/functional_tests/test_v2_orchestration_progress_lane.mjs index 853bc155e..791af2eec 100644 --- a/functional_tests/test_v2_orchestration_progress_lane.mjs +++ b/functional_tests/test_v2_orchestration_progress_lane.mjs @@ -1,10 +1,11 @@ // test_v2_orchestration_progress_lane.mjs -// Version: 0.261.204 +// Version: 0.261.318 // Implemented in: 0.261.204 +// Agent and tabular cards removed in: 0.261.318 // Executes the real activity-lane fold (lib/activityLanes.ts) over the reasoning steps an // orchestrated turn produces. The orchestration lane still claims those steps, so they are never // counted as agent or tabular work, but it draws no progress card: the plan card and the reasoning -// toggle already show that progress. Tabular and agent work keep their cards. +// toggle already show that progress. Agent and tabular details also stay under the toggle. import assert from 'node:assert/strict'; import test from 'node:test'; @@ -93,24 +94,24 @@ test('an agent hand-off inside an orchestrated turn does not bring back a card', assert.equal(progress.lane.showsCard, false); }); -test('tabular and agent work keep their progress cards', () => { +test('tabular and agent work keep their lanes without progress cards', () => { const tabular = buildLaneProgress([tabularThought()], { live: true }); assert.equal(tabular?.lane.key, 'tabular'); - assert.equal(tabular.lane.showsCard, true); + assert.equal(tabular.lane.showsCard, false); const handedOff = buildLaneProgress([ { step_type: 'agent_tool_call', content: 'Sending to agent analyst' }, tabularThought('completed'), ]); assert.equal(handedOff?.lane.key, 'tabular'); - assert.equal(handedOff.lane.showsCard, true); + assert.equal(handedOff.lane.showsCard, false); const agent = buildLaneProgress( [{ step_type: 'agent_tool_call', content: 'Sending to agent researcher' }], { live: true }, ); assert.equal(agent?.lane.key, 'agent'); - assert.equal(agent.lane.showsCard, true); + assert.equal(agent.lane.showsCard, false); }); test('ordinary reasoning still forms no lane at all', () => { diff --git a/ui_tests/test_v2_orchestration_streaming_bubble.py b/ui_tests/test_v2_orchestration_streaming_bubble.py index f929c4867..4339507f6 100644 --- a/ui_tests/test_v2_orchestration_streaming_bubble.py +++ b/ui_tests/test_v2_orchestration_streaming_bubble.py @@ -1,8 +1,9 @@ # test_v2_orchestration_streaming_bubble.py """ Browser regression for the V2 orchestration streaming bubble. -Version: 0.261.256 +Version: 0.261.318 Implemented in: 0.261.204 +Agent and tabular cards removed in: 0.261.318 An orchestrated turn shows one progress indicator at a time. @@ -16,7 +17,9 @@ status line now says what the run is doing: "Starting", the running step's kind of work and title, and "Preparing the answer" once every step has settled. The run's notices stay with the finished answer's reasoning steps. Any stream that is not the run's own, such as a chat reply sent while a -run waits, still shows "Thinking". Tabular analysis keeps its progress card. +run waits, still shows "Thinking". Agent and tabular activity now also shows only the reasoning +toggle and lightweight Thinking indicator, without a thought-summary progress card. Their details +remain readable during streaming and in finished/remounted responses, including failed tools. The production controller, stores, SSE reader, MessageList and plan card run in Chromium with production CSS. Only HTTP is deterministic: the plan and run streams are held open and fed one @@ -451,25 +454,109 @@ def test_a_stream_that_is_not_the_run_still_shows_thinking_beside_its_card(bubbl expect(thinking).to_have_count(0) -def test_tabular_streaming_bubble_keeps_its_progress_card(bubble_ui): +@pytest.mark.parametrize("lane", ["agent", "tabular"]) +@pytest.mark.parametrize("viewport", [ + {"width": 1280, "height": 900}, + {"width": 390, "height": 844}, +], ids=["desktop", "mobile"]) +def test_agent_and_tabular_reasoning_stays_accessible_without_progress_cards(bubble_ui, lane, viewport): page, api = bubble_ui - page.evaluate("""() => window.OrchHarness.stores.chat.useChatStore.setState({ - streaming: true, - thoughts: [{ - id: '0', - title: 'tabular_analysis', - content: 'Reading the quarterly workbook.', - stepType: 'tabular_analysis', - activity: { - activity_key: 'call-1', kind: 'tabular_tool_invocation', - title: 'describe_tabular_file', status: 'running', state: 'running', - lane_key: 'tabular', lane_label: 'Tabular', - plugin_name: 'TabularProcessingPlugin', function_name: 'describe_tabular_file', + page.set_viewport_size(viewport) + step_type = "agent_tool_call" if lane == "agent" else "tabular_analysis" + content = "Reading the quarterly workbook." if lane == "tabular" else "Searching the quarterly reports." + hand_off = "Sending to agent analyst" + thoughts = [ + {"id": "hand-off", "title": "agent_tool_call", "stepType": "agent_tool_call", "content": hand_off}, + { + "id": "tool", "title": step_type, "stepType": step_type, "content": content, + "activity": { + "activity_key": "call-1", + "kind": "tabular_tool_invocation" if lane == "tabular" else "agent_tool_invocation", + "title": "describe_tabular_file" if lane == "tabular" else "Search", + "status": "running", "state": "running", "lane_key": lane, }, - }], - })""") - expect(page.get_by_role("progressbar", name="Tabular analysis progress")).to_be_visible() - expect(page.get_by_text("Tabular analysis", exact=True)).to_be_visible() - expect(page.get_by_text("Current tabular step: describe_tabular_file", exact=True)).to_be_visible() - expect(toggle(page, 1)).to_be_visible() + }, + ] + page.evaluate( + """(thoughts) => window.OrchHarness.stores.chat.useChatStore.setState({ + streaming: true, thoughts, + })""", + thoughts, + ) + steps = toggle(page, 2) + expect(steps).to_be_visible() + expect(steps).to_have_attribute("aria-expanded", "false") expect(page.get_by_text("Thinking", exact=True)).to_be_visible() + expect_no_agent_or_tabular_progress_card(page) + expect(page.get_by_text(content, exact=True)).to_have_count(0) + expect_toggle_first(steps) + + steps.focus() + steps.press("Enter") + expect(steps).to_have_attribute("aria-expanded", "true") + expect(page.get_by_text(hand_off, exact=True)).to_be_visible() + expect(page.get_by_text(content, exact=True)).to_be_visible() + expect_no_agent_or_tabular_progress_card(page) + steps.press("Space") + expect(steps).to_have_attribute("aria-expanded", "false") + expect(page.get_by_text(content, exact=True)).to_have_count(0) + + failure = "The tool could not finish. Its failure details remain in the reasoning steps." + thoughts[1]["activity"].update({"status": "failed", "state": "failed"}) + thoughts[1]["content"] = failure + page.evaluate( + "(thoughts) => window.OrchHarness.stores.chat.useChatStore.setState({thoughts})", + thoughts, + ) + expect_no_agent_or_tabular_progress_card(page) + expect(page.get_by_text(failure, exact=True)).to_have_count(0) + steps.click() + expect(page.get_by_text(failure, exact=True)).to_be_visible() + expect_no_agent_or_tabular_progress_card(page) + + saved = { + "id": "agent-tabular-answer", "conversation_id": CONVERSATION, + "role": "assistant", "content": ANSWER, + "thoughts": [ + *thoughts, + {"id": "answer", "title": "generation", "stepType": "generation", + "content": "Agent responded with the available evidence."}, + ], + } + page.evaluate( + """(message) => window.OrchHarness.stores.chat.useChatStore.setState({ + streaming: false, thoughts: [], messages: [message], + })""", + saved, + ) + expect(page.get_by_text(ANSWER, exact=True)).to_be_visible() + expect(page.get_by_text("Thinking", exact=True)).to_have_count(0) + saved_steps = toggle(page, 3) + expect(saved_steps).to_have_attribute("aria-expanded", "false") + saved_steps.click() + expect(page.get_by_text(failure, exact=True)).to_be_visible() + expect_no_agent_or_tabular_progress_card(page) + + page.evaluate("""() => { + const H = window.OrchHarness; + H.unmount('mount-a'); + H.mount('mount-a', 'MessageList'); + }""") + reopened_steps = toggle(page, 3) + expect(reopened_steps).to_have_attribute("aria-expanded", "false") + expect(page.get_by_text(failure, exact=True)).to_have_count(0) + reopened_steps.click() + expect(page.get_by_text(failure, exact=True)).to_be_visible() + expect(page.get_by_text(ANSWER, exact=True)).to_be_visible() + expect_no_agent_or_tabular_progress_card(page) + + +def expect_no_agent_or_tabular_progress_card(page): + """No part of either thought-summary card appears, even when reasoning is expanded.""" + expect(page.get_by_role("progressbar")).to_have_count(0) + expect(page.get_by_text(re.compile(r"^(Agent progress|Tabular analysis)( complete)?$"))).to_have_count(0) + expect(page.get_by_text(re.compile(r"^Current (tool|tabular step): "))).to_have_count(0) + expect(page.get_by_text(re.compile(r"^\d{1,3}%$"))).to_have_count(0) + expect(page.get_by_text(re.compile(r"^\d+/\d+ (tools?|steps?|tool calls?)\b"))).to_have_count(0) + expect(page.get_by_text(re.compile(r"^(Agent progress|Tabular analysis) captured for this response$"))).to_have_count(0) + expect(page.get_by_text("Completed with issues", exact=True)).to_have_count(0)