Skip to content

Restore single-line compact rows in the dashboard resource list - #21880

Merged
Ankit Jain (radical) merged 6 commits into
mainfrom
sebros/resources-compact-view-regression-4abee4
Nov 7, 2026
Merged

Ankit Jain (radical) merged 6 commits into
mainfrom
sebros/resources-compact-view-regression-4abee4

Conversation

@sebastienros

@sebastienros Sébastien Ros (sebastienros) commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

The resource list's Compact density lost its one-line rows in #21727. That PR removed the compact branch of the row markup and switched .density-compact .resource-row-text to flex-direction: column, so compact rows became the same two-line rows as Comfortable ("● Running · 1 minute ago"), only shorter. They then also picked up the "No logs" / "Not applicable" badges added in #21819. #21727 also dropped Spacious's third detail line. That left the three densities differing only in row height and font size, although ResourceListDensity still documents one, two and three lines per resource.

This PR restores the documented densities:

  • Compact: one line per resource. The icon and name are followed by aligned type and state-dot columns. In the sidebar, a row's actions replace the columns while it's hovered or focused, or while its menu is open, as before Add an expandable resource pane and contextual resource actions #21727. The expand toggle of parent rows stays visible, and touch screens (hover: none) always show the actions. Availability badges don't fit on one line: an unavailable row is muted, and the reason stays in its tooltip and accessible description. Unviewed-error badges sit just before the columns, so the columns stay aligned. A badge keeps the same place while the row's actions show, so it never moves out from under the pointer: it takes whichever place is further from the row's end, clear of the columns or clear of the actions.
  • Comfortable: unchanged.
  • Spacious: sidebar rows have a third line again. It shows an unhealthy health status if there is one, otherwise the primary endpoint, otherwise the source. When no endpoint can be opened as a link, such as a tcp:// endpoint, the first endpoint is used, as in the expanded list's endpoint column. Query values are masked. The endpoint and source can take keyboard focus, which shows the full value in a tooltip and gives it an accessible label.

In the expanded resource list (full canvas), compact rows use the same single-line identity. From 960px wide, the identity column can grow by the width of the type and state columns, so names aren't squeezed. That extra width only raises the column's maximum, never its minimum, so rows don't overflow the list at narrower widths. Actions there stay visible all the time.

Persistent types such as "RabbitMQ (persistent)" widen every row's type column together, so the columns stay aligned. The column is 128px where rows can be hovered. On touch screens, where the action rail is always showing, it stays at 64px at the default 420px pane width so names keep their room.

Screenshots / Recordings

The resource pane at its default 420px width, using the TestShop playground, before (main) and after:

  • First row (before): all three densities are the same two-line row.
  • Second row (after): Compact is one line with the actions hidden at rest, Comfortable is unchanged, and Spacious has its third line.
  • Third row: compact on Structured logs. Before, rows had "No logs" badges. After, those rows are muted instead. The last panel adds two unviewed-error badges, injected into the page because the sample app logs no errors.

Before and after of the resource list in the Compact, Comfortable and Spacious densities, plus compact rows on Structured logs

Compact rows with an unviewed error, at rest and hovered. On main, the badge jumped about 62px toward the actions when a row was hovered, with a persistent type in the list. It now keeps its place:

Compact rows with an unviewed-error badge at rest and hovered: before, the badge jumps toward the actions; after, it keeps its place, with and without a persistent type in the list

Validation

  • Updated the ResourcesLayoutTests that asserted the two-line compact row (CompactDensity_ShowsOneLineWithTypeAndStateColumns, WideRowMetadata_PreservesRuntimeStateAndAccessibleHealthWithRelativeStart, AppHost_ContainerLifetimeQualifiesTypeAndBadgesResourceGlyph). Added ResourceRowAvailabilityTests.CompactDensity_UnavailableRowKeepsReasonWithoutBadge and ResourcesLayoutTests.SpaciousSidebarDetail_PrefersUnhealthyStatusThenEndpointThenSource.
  • The 264 tests in ResourcesLayoutTests, ResourceRowAvailabilityTests, MainLayoutTests, DashboardViewPreferencesTests and SettingsDialogTests pass.
  • Checked manually against the TestShop playground with Playwright at the default 420px pane width:
    • In every density, no row element overlaps another or spills out of its row.
    • Compact: the rest state, hover on a row and on a parent row, keyboard focus, an open "…" menu, an error badge (simulated) and touch emulation.
    • The expanded list in every density.
  • Updated ResourcePaneResponsiveTests for the restored geometry:
    • 32px compact rows, and 22px, 32px and 40px icons for Compact, Comfortable and Spacious.
    • Spacious rows are 76px in the sidebar and 60px in the expanded list.
    • Compact sidebar rows show only the expand-toggle column at rest, and the full action rail on hover or keyboard focus.
    • A new horizontal-overflow check, with 960px and 990px stages. With the previous track minimums it fails at 960px (980px of content).
  • Added ResourcePaneResponsiveTests.CompactSidebar_ErrorBadgeKeepsItsPlaceWhileActionsShow. It seeds error logs and checks five states: at rest, the row hovered, the badge hovered, the row focused, and the menu open. In each, the badge must not move or be covered, must line up with its spacer, and must not overlap a column or an action. It runs with and without a persistent type in the list, and fails against the previous styles.
  • 75 of the 80 ResourcePaneResponsiveTests pass locally. 4 failures come from RowComposition_AlignsStatusAndSingleLineDetailsWithAccessibleContrast and Accessibility_PaneAndPopoversHaveNoBlockingWcagViolations at 390px, which fail the same way on main. Resizer_DoubleClickRestoresMinimumAcrossPresentationAndNavigation failed once in the full run because a drag didn't take effect, and passed when rerun on its own.

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

#21727 removed the compact branch of the resource row and stacked compact
rows into two lines, so Compact became a shorter Comfortable with
"Running" text, and later picked up the availability badges from #21819.
It also dropped Spacious's third detail line, leaving the three densities
different only in row height and font size.

Compact rows are one line again: the name, then aligned type and state
columns. In the sidebar, actions replace the columns while a row is
hovered or focused or its menu is open; the parent expand toggle stays
visible and touch screens always show the actions. The expanded list
keeps its actions visible and widens its identity column for the extra
columns. Availability is shown by the muted row and its tooltip, and
error badges sit before the columns.

Spacious sidebar rows show a third line again with an unhealthy health
status, the primary endpoint (query values masked) or the source.
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21880

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21880"

@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The updated layout conflicts with existing browser-test assertions and retains horizontal overflow, keyboard-inaccessible source details, and missing TCP/Redis endpoint details.

5 open issues
What changed in this PR

Restores distinct dashboard resource-list densities after earlier layout changes made Compact and Spacious resemble Comfortable.

Changes:

  • Restores single-line Compact rows with aligned type/state columns and contextual sidebar actions.
  • Adds Spacious sidebar details, prioritizing health status, then a masked endpoint, then source.
  • Updates component tests for Compact identity, persistence, and availability descriptions.
File Description
tests/​Aspire.Dashboard.Components.Tests/​Layout/​ResourcesLayoutTests.cs Updates Compact markup, state, and persistent-type assertions.
tests/​Aspire.Dashboard.Components.Tests/​Layout/​ResourceRowAvailabilityTests.cs Verifies unavailable Compact rows retain explanations without badges.
src/​Aspire.Dashboard/​Components/​Layout/​ResourcesLayout.razor.css Adjusts density sizing, aligned columns, action visibility, and full-canvas tracks.
src/​Aspire.Dashboard/​Components/​Layout/​ResourcesLayout.razor Restores Compact markup and Spacious detail selection.

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

- Fall back to any endpoint (including tcp:// endpoints that aren't links)
  for the spacious sidebar detail line, matching the full-canvas endpoint
  column.
- Make the spacious endpoint and source detail keyboard-focusable, with the
  full value in its tooltip and accessible label, and a focus ring.
- Add the compact type and state column width only to the full-canvas name
  track's maximum, so rows no longer overflow at 540-584, 720-730 and
  960-998px.
- Update the responsive Playwright tests for 32px compact rows, density-keyed
  icon sizes, the separate spacious sidebar and full-canvas heights, and
  hover/focus-revealed compact sidebar actions. Add a horizontal-overflow
  check and 960/990px stages.
- Add bUnit coverage for the spacious detail priority: unhealthy status, then
  endpoint (masked), then source, omitted for parameters and the full canvas.
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In compact sidebar rows, the unviewed-error badge sat before the type and
state columns at rest, but moved next to the actions while they showed. With
a persistent type in the list (a 128px type column) that was a 62px jump, which
also slid the badge out from under a pointer that was on it.

The badge now takes whichever place is further from the row's end in both
states: clear of the columns or clear of the actions. Its spacer moves with
it, so the name still stops before the badge.

Add a Playwright test that seeds error logs and checks, at rest, on row hover,
on badge hover, on focus and with the menu open, that the badge doesn't move,
isn't covered, sits over its spacer and overlaps no column or action, with and
without a persistent type.
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

URL masking leaves credentials and fragment secrets exposed in detail text, tooltips, and accessible labels, while compact-sidebar type/state tooltips are unreachable.

5 open issues

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

…pane

- Open on the compact full-page list of all resources, with no tab selected. A reload keeps
  the view the browser tab showed, and /graph opens the overview with the graph.
- Escape closes the resource view like the Close button, whose tooltip names the key.
  Open popups, popovers, menus and tooltips keep Escape for themselves.
- While the content is closed, no tab is shown selected and rows show no unavailable
  telemetry state. Selecting resources reopens the last tab; selecting a tab opens it for
  all resources.
- Replace the kind toggles with a Telemetry sources option in the filter's resource types,
  and rename Select all to All resources, with the header using the same icon.
- Show when a stopped resource stopped, keep a row's second line on one line with an
  ellipsis before its error badge, and pin metrics for the selected resources only.
- In the full-page list, use the resources grid's URLs column (links and a +N list) and
  drop the console logs, JSON and +N actions, which stay in the row's menu.
- Address review feedback: mask URL credentials in the spacious detail, keep the compact
  expand toggle in place, and name a compact row's type and state in its tooltip.
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The changes expose sensitive URL query values, remove keyboard access to truncated non-link endpoints, and omit selected resources without telemetry from metric pins.

3 open issues
5 resolved since last review
2 files not reviewed
  • src/Aspire.Dashboard/Resources/ControlsStrings.Designer.cs: Generated file
  • src/Aspire.Dashboard/Resources/Layout.Designer.cs: Generated file

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Tests selector

15 / 101 PR test projects · 2 PR jobs, from 77 changed files.

Selected PR test projects (15 / 101)

Aspire.Dashboard.Components.Tests, Aspire.Dashboard.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Native.Core.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Playground.Tests, Aspire.Templates.Tests, Infrastructure.Tests

Selected PR jobs (2)

extension-e2e, native-dashboard-validation


How these were chosen — grouped by what changed

⚠️ 10 of the 15 selected test projects come from a single change — src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor.

🔧 src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor (changed source)
→ 10 via the project graph: Aspire.Hosting.Azure.Kubernetes.Tests (3 hops), Aspire.Hosting.Azure.Tests (2 hops), Aspire.Hosting.Blazor.Tests (2 hops), Aspire.Hosting.Dotnet.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Radius.Tests (2 hops), Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Playground.Tests (2 hops)

📦 affected project Aspire.Dashboard
→ 1 test: Aspire.Templates.Tests

🔧 src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.js (changed source)
→ 1 directly: Infrastructure.Tests

🔧 src/Aspire.Dashboard/Resources/ControlsStrings.resx (changed source)
→ 1 directly: Infrastructure.Tests

🔧 src/Aspire.Dashboard/Resources/Layout.resx (changed source)
→ 1 directly: Infrastructure.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Controls/UrlsColumnDisplayTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Layout/ResourceRowAvailabilityTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Layout/ResourcesLayoutCloseTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Layout/ResourcesLayoutTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Model/DashboardViewPreferencesTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Pages/MetricsTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Tests/Components/UrlsColumnDisplayTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Model/MetricPinStoreTests.cs (changed test)
→ 1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Hosting.Native.Core.Tests/AotSmoke/NativeDashboardSmoke.mts (changed test)
→ 1 directly: Aspire.Hosting.Native.Core.Tests

Job reasons

Job Triggered by
extension-e2e • src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor, src/Aspire.Dashboard/Components/Controls/Chart/ChartContainer.razor.cs, src/Aspire.Dashboard/Components/Controls/Chart/MeterOverview.razor, src/Aspire.Dashboard/Components/Controls/Chart/MeterOverview.razor.cs, src/Aspire.Dashboard/Components/Controls/Chart/MetricPinButton.razor, src/Aspire.Dashboard/Components/Controls/DetailPageCloseButton.razor, src/Aspire.Dashboard/Components/Controls/DetailPageCloseButton.razor.cs, src/Aspire.Dashboard/Components/Controls/MetricGroupList.razor.cs, src/Aspire.Dashboard/Components/Controls/ResourcePaneFilter.razor, src/Aspire.Dashboard/Components/Controls/ResourcePaneFilter.razor.cs, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.cs, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.css, src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.js, src/Aspire.Dashboard/Components/Legacy/Pages/Metrics.razor, src/Aspire.Dashboard/Components/Pages/GraphRedirect.razor.cs, src/Aspire.Dashboard/Components/Pages/Metrics.razor, src/Aspire.Dashboard/Components/Pages/Metrics.razor.cs, src/Aspire.Dashboard/Components/Pages/ResourceOverview.razor, src/Aspire.Dashboard/Components/ResourcesGridColumns/UrlsColumnDisplay.razor, src/Aspire.Dashboard/Components/ResourcesGridColumns/UrlsColumnDisplay.razor.cs, src/Aspire.Dashboard/Model/DashboardViewPreferences.cs, src/Aspire.Dashboard/Model/MetricPin.cs, src/Aspire.Dashboard/Model/MetricPinStore.cs, src/Aspire.Dashboard/Resources/ControlsStrings.Designer.cs, src/Aspire.Dashboard/Resources/ControlsStrings.resx, src/Aspire.Dashboard/Resources/Layout.Designer.cs, src/Aspire.Dashboard/Resources/Layout.resx, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.cs.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.de.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.es.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.fr.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.it.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.ja.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.ko.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.pl.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.pt-BR.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.ru.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.tr.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.zh-Hans.xlf, src/Aspire.Dashboard/Resources/xlf/ControlsStrings.zh-Hant.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.cs.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.de.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.es.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.fr.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.it.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.ja.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.ko.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.pl.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.pt-BR.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.ru.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.tr.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.zh-Hans.xlf, src/Aspire.Dashboard/Resources/xlf/Layout.zh-Hant.xlf, src/Aspire.Dashboard/Serialization/DashboardJsonSerializerContext.cs, src/Aspire.Dashboard/Utils/BrowserStorageKeys.cs
• affected project Aspire.Dashboard
native-dashboard-validation • tests/Aspire.Dashboard.Playwright.Tests/Integration/Playwright/Infrastructure/PlaywrightTestsBase.cs, tests/Aspire.Dashboard.Playwright.Tests/Integration/Playwright/Infrastructure/ResourcePaneNavigation.cs
• affected project Aspire.Dashboard

Selection computed for commit c7efb1a.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unpublished findings involving navigation and persistence races, unreliable test readiness, and quadratic metric lookups require human assessment.

No open issues 🎉

3 resolved since last review
2 files not reviewed
  • src/Aspire.Dashboard/Resources/ControlsStrings.Designer.cs: Generated file
  • src/Aspire.Dashboard/Resources/Layout.Designer.cs: Generated file

Review effort: Balanced

@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@radical
Ankit Jain (radical) merged commit db27327 into main Nov 7, 2026
317 of 320 checks passed
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 17.1 milestone Nov 7, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor

Pull request created: #2088

Generated by PR Documentation Check · copilot · auto · 19.8 AIC · ⌖ 1.03 AIC · ⊞ 7.4K

@aspire-repo-bot

Copy link
Copy Markdown
Contributor

📝 Documentation has been drafted in microsoft/aspire.dev#2088 targeting release/17.0.

Added a "Resource list density" section to dashboard/explore.mdx describing Compact/Comfortable/Spacious. Triggered signals: dashboard_user_facing_page_changed (Components/Pages/ResourceOverview.razor), new_public_type (ResourcePaneFilter.razor.cs FilterOption).

Note

This draft PR needs human review before merging.

Mitch Denny (mitchdenny) pushed a commit that referenced this pull request Nov 8, 2026
…21924)

## Description

The scheduled [Outerloop Tests run on
main](https://github.com/microsoft/aspire/actions/runs/42437120205)
failed in two jobs after #21880:

- [Templates
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013):
all 10 `StarterTemplateWithRedisCacheTests` and
`StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for
`.resource-row-sub > .state-dot + span` in
`DashboardResourceAssertions.CheckResourcesAsync`.
- [Hosting.Testing
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062):
`DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
couldn't find the text "Dashboard test initial" in the resource row. The
row itself was there.

## Failure stats (14d, attempt 1)

`Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25
06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1
jobs that ran (success or failure). Hits: the failed job's log matches
the signature. Jobs that lost their runner, which have no log, are
excluded.

| | |
|---|---|
| Signature | Templates: `.resource-row-sub > .state-dot + span`
timeout; Hosting.Testing: `Dashboard test initial` not found |
| Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing
(ubuntu-latest), main |
| main pushes: hits / runs | 2 / 28 (7.14%) |
| main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on
2026-11-08. Templates also failed on main every scheduled run from
2026-10-10 to 2026-11-04 with other causes |
| PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) |
| Combined rate | 2 / 28 = **7.14%** |
| Flake check | main: failed on all 4 attempts of the run (not a flake)
|
| First / last hit | 2026-11-08 / 2026-11-08 |
| Still hitting main? | Hit on the latest scheduled main run
(2026-11-08); no main run since |
| Fix evidence | Local: default-density helper tests fail with the old
helper and pass with the new one. Outerloop dispatched on this PR's
head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. |

Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits):

| Date (UTC) | Branch | Job | Signature excerpt |
|---|---|---|---|
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928)
| **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard
test initial", Exact) element(s) not found` |
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061)
| **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot
+ span timeout` |

**Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled
main run failed both jobs on **all 4 attempts** with these signatures.
The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the
signature never appeared earlier in the window. Earlier Templates
failures (2026-10-27 to 2026-11-04) had other causes.

<!-- /failure-stats -->

**Why:** #21880 deliberately brought back single-line rows for
**Compact**, the dashboard's default density. A compact row shows the
state as a dot in a `.resource-row-meta-state` column, and the state's
name is in that column's `data-tooltip`. There's no `.resource-row-sub`
and no visible state text. These browser tests open the dashboard at its
default density, so they no longer found the state.
`Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase`
pins those tests to Comfortable unless they opt into the default. This
is a deterministic break, not a flake: the previous scheduled runs on
main
([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779),
before #21880) passed, and the only dashboard change in between is
#21880.

**Fix:** the tests now match the intended markup, and no assertions are
dropped:

- `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at
any density. In compact rows it reads the state column's tooltip. In
other densities it reads the text after the dot. `RowState(row, state)`
finds the element that shows a given state.
- `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while
a row hasn't rendered a state yet.
- `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
waits for the initial and then the updated state with `RowState`.
-
`TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards`
now runs at both the default (compact) density and Comfortable, and
checks `RowState` directly, so the helper is covered against the real
dashboard at the density the template tests use.

## Confidence

- Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old
helper, the two new default-density cases fail with the same `Timeout
... exceeded` as the Templates job.
- [Outerloop Tests dispatched on this
branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to
cover the Templates and Hosting.Testing jobs end to end. I didn't run
those two locally; they need the full template build and container
runtime.

**How we stop this recurring:** the shared helper's own test now runs at
the dashboard's default density, so a markup change to default rows
fails that test against the dashboard alone, which is fast to run
locally while changing row markup and points straight at the helper.
It's still an outerloop test, so it doesn't gate PR CI.

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
  - [x] No

**🤖 Posted by AspireAgent (AI Agent)**

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Maddy Montaquila (maddymontaquila) pushed a commit that referenced this pull request Nov 8, 2026
Brings in main's dashboard changes, including restored single-line compact
rows (#21880), and keeps this branch's simplification on top:

- Main moved the resource kinds into the filter, as a "Telemetry sources" option
  after the resource types. That replaces this branch's "Show" section, which
  is dropped.
- The "All resources" row stays in place of main's "All resources" button, so
  its strings ("All selected") and styles are removed. Tests that used the
  button use the row, and the Playwright tests are mapped to it too.
- Main's row actions (toggle, primary endpoint, commands, menu) and its URLs
  column with "+N" replace this branch's single clickable URL and fixed slots.
  The Source column keeps its type prefix and wider tracks.
- Main's row time (since stopping, once a resource has stopped) keeps this
  branch's short "7m ago" form; the tooltip has the full wording.
- Rows still have no availability badges, and the page-level close button is
  still limited to Overview, Parameters and Terminal.
- In the full list, a compact row's type column is hidden because the Source
  column shows the type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mitch Denny (mitchdenny) pushed a commit that referenced this pull request Nov 10, 2026
## Description

The resource list's **Compact** density lost its one-line rows in
#21727. That PR removed the compact branch of the row markup and
switched `.density-compact .resource-row-text` to `flex-direction:
column`, so compact rows became the same two-line rows as Comfortable
("● Running · 1 minute ago"), only shorter. They then also picked up the
"No logs" / "Not applicable" badges added in #21819. #21727 also dropped
**Spacious**'s third detail line. That left the three densities
differing only in row height and font size, although
`ResourceListDensity` still documents one, two and three lines per
resource.

This PR restores the documented densities:

- **Compact**: one line per resource. The icon and name are followed by
aligned type and state-dot columns. In the sidebar, a row's actions
replace the columns while it's hovered or focused, or while its menu is
open, as before #21727. The expand toggle of parent rows stays visible,
and touch screens (`hover: none`) always show the actions. Availability
badges don't fit on one line: an unavailable row is muted, and the
reason stays in its tooltip and accessible description. Unviewed-error
badges sit just before the columns, so the columns stay aligned. A badge
keeps the same place while the row's actions show, so it never moves out
from under the pointer: it takes whichever place is further from the
row's end, clear of the columns or clear of the actions.
- **Comfortable**: unchanged.
- **Spacious**: sidebar rows have a third line again. It shows an
unhealthy health status if there is one, otherwise the primary endpoint,
otherwise the source. When no endpoint can be opened as a link, such as
a `tcp://` endpoint, the first endpoint is used, as in the expanded
list's endpoint column. Query values are masked. The endpoint and source
can take keyboard focus, which shows the full value in a tooltip and
gives it an accessible label.

In the expanded resource list (full canvas), compact rows use the same
single-line identity. From 960px wide, the identity column can grow by
the width of the type and state columns, so names aren't squeezed. That
extra width only raises the column's maximum, never its minimum, so rows
don't overflow the list at narrower widths. Actions there stay visible
all the time.

Persistent types such as "RabbitMQ (persistent)" widen every row's type
column together, so the columns stay aligned. The column is 128px where
rows can be hovered. On touch screens, where the action rail is always
showing, it stays at 64px at the default 420px pane width so names keep
their room.

### Screenshots / Recordings

The resource pane at its default 420px width, using the TestShop
playground, before (main) and after:

- **First row (before):** all three densities are the same two-line row.
- **Second row (after):** Compact is one line with the actions hidden at
rest, Comfortable is unchanged, and Spacious has its third line.
- **Third row:** compact on Structured logs. Before, rows had "No logs"
badges. After, those rows are muted instead. The last panel adds two
unviewed-error badges, injected into the page because the sample app
logs no errors.

![Before and after of the resource list in the Compact, Comfortable and
Spacious densities, plus compact rows on Structured
logs](https://github.com/user-attachments/assets/d31996b1-5001-4f8e-9605-8c2b3ae9601f)

Compact rows with an unviewed error, at rest and hovered. On main, the
badge jumped about 62px toward the actions when a row was hovered, with
a persistent type in the list. It now keeps its place:

![Compact rows with an unviewed-error badge at rest and hovered: before,
the badge jumps toward the actions; after, it keeps its place, with and
without a persistent type in the
list](https://github.com/user-attachments/assets/b88ffc0c-fbb0-4cca-b4dd-53e1ccd2c578)

### Validation

- Updated the `ResourcesLayoutTests` that asserted the two-line compact
row (`CompactDensity_ShowsOneLineWithTypeAndStateColumns`,
`WideRowMetadata_PreservesRuntimeStateAndAccessibleHealthWithRelativeStart`,
`AppHost_ContainerLifetimeQualifiesTypeAndBadgesResourceGlyph`). Added
`ResourceRowAvailabilityTests.CompactDensity_UnavailableRowKeepsReasonWithoutBadge`
and
`ResourcesLayoutTests.SpaciousSidebarDetail_PrefersUnhealthyStatusThenEndpointThenSource`.
- The 264 tests in `ResourcesLayoutTests`,
`ResourceRowAvailabilityTests`, `MainLayoutTests`,
`DashboardViewPreferencesTests` and `SettingsDialogTests` pass.
- Checked manually against the TestShop playground with Playwright at
the default 420px pane width:
- In every density, no row element overlaps another or spills out of its
row.
- Compact: the rest state, hover on a row and on a parent row, keyboard
focus, an open "…" menu, an error badge (simulated) and touch emulation.
  - The expanded list in every density.
- Updated `ResourcePaneResponsiveTests` for the restored geometry:
- 32px compact rows, and 22px, 32px and 40px icons for Compact,
Comfortable and Spacious.
  - Spacious rows are 76px in the sidebar and 60px in the expanded list.
- Compact sidebar rows show only the expand-toggle column at rest, and
the full action rail on hover or keyboard focus.
- A new horizontal-overflow check, with 960px and 990px stages. With the
previous track minimums it fails at 960px (980px of content).
- Added
`ResourcePaneResponsiveTests.CompactSidebar_ErrorBadgeKeepsItsPlaceWhileActionsShow`.
It seeds error logs and checks five states: at rest, the row hovered,
the badge hovered, the row focused, and the menu open. In each, the
badge must not move or be covered, must line up with its spacer, and
must not overlap a column or an action. It runs with and without a
persistent type in the list, and fails against the previous styles.
- 75 of the 80 `ResourcePaneResponsiveTests` pass locally. 4 failures
come from
`RowComposition_AlignsStatusAndSingleLineDetailsWithAccessibleContrast`
and `Accessibility_PaneAndPopoversHaveNoBlockingWcagViolations` at
390px, which fail the same way on main.
`Resizer_DoubleClickRestoresMinimumAcrossPresentationAndNavigation`
failed once in the full run because a drag didn't take effect, and
passed when rerun on its own.

Fixes # (issue)

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
    - If yes, did you have an API Review for it?
      - [ ] Yes
      - [ ] No
- Did you add `<remarks />` and `<code />` elements on your triple slash
comments?
      - [ ] Yes
      - [ ] No
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
    - If yes, have you done a threat model and had a security review?
      - [ ] Yes
      - [ ] No
  - [x] No

---------

Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com>
(cherry picked from commit db27327)
Mitch Denny (mitchdenny) pushed a commit that referenced this pull request Nov 10, 2026
…21924)

## Description

The scheduled [Outerloop Tests run on
main](https://github.com/microsoft/aspire/actions/runs/42437120205)
failed in two jobs after #21880:

- [Templates
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013):
all 10 `StarterTemplateWithRedisCacheTests` and
`StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for
`.resource-row-sub > .state-dot + span` in
`DashboardResourceAssertions.CheckResourcesAsync`.
- [Hosting.Testing
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062):
`DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
couldn't find the text "Dashboard test initial" in the resource row. The
row itself was there.

## Failure stats (14d, attempt 1)

`Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25
06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1
jobs that ran (success or failure). Hits: the failed job's log matches
the signature. Jobs that lost their runner, which have no log, are
excluded.

| | |
|---|---|
| Signature | Templates: `.resource-row-sub > .state-dot + span`
timeout; Hosting.Testing: `Dashboard test initial` not found |
| Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing
(ubuntu-latest), main |
| main pushes: hits / runs | 2 / 28 (7.14%) |
| main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on
2026-11-08. Templates also failed on main every scheduled run from
2026-10-10 to 2026-11-04 with other causes |
| PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) |
| Combined rate | 2 / 28 = **7.14%** |
| Flake check | main: failed on all 4 attempts of the run (not a flake)
|
| First / last hit | 2026-11-08 / 2026-11-08 |
| Still hitting main? | Hit on the latest scheduled main run
(2026-11-08); no main run since |
| Fix evidence | Local: default-density helper tests fail with the old
helper and pass with the new one. Outerloop dispatched on this PR's
head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. |

Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits):

| Date (UTC) | Branch | Job | Signature excerpt |
|---|---|---|---|
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928)
| **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard
test initial", Exact) element(s) not found` |
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061)
| **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot
+ span timeout` |

**Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled
main run failed both jobs on **all 4 attempts** with these signatures.
The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the
signature never appeared earlier in the window. Earlier Templates
failures (2026-10-27 to 2026-11-04) had other causes.

<!-- /failure-stats -->

**Why:** #21880 deliberately brought back single-line rows for
**Compact**, the dashboard's default density. A compact row shows the
state as a dot in a `.resource-row-meta-state` column, and the state's
name is in that column's `data-tooltip`. There's no `.resource-row-sub`
and no visible state text. These browser tests open the dashboard at its
default density, so they no longer found the state.
`Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase`
pins those tests to Comfortable unless they opt into the default. This
is a deterministic break, not a flake: the previous scheduled runs on
main
([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779),
before #21880) passed, and the only dashboard change in between is
#21880.

**Fix:** the tests now match the intended markup, and no assertions are
dropped:

- `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at
any density. In compact rows it reads the state column's tooltip. In
other densities it reads the text after the dot. `RowState(row, state)`
finds the element that shows a given state.
- `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while
a row hasn't rendered a state yet.
- `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
waits for the initial and then the updated state with `RowState`.
-
`TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards`
now runs at both the default (compact) density and Comfortable, and
checks `RowState` directly, so the helper is covered against the real
dashboard at the density the template tests use.

## Confidence

- Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old
helper, the two new default-density cases fail with the same `Timeout
... exceeded` as the Templates job.
- [Outerloop Tests dispatched on this
branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to
cover the Templates and Hosting.Testing jobs end to end. I didn't run
those two locally; they need the full template build and container
runtime.

**How we stop this recurring:** the shared helper's own test now runs at
the dashboard's default density, so a markup change to default rows
fails that test against the dashboard alone, which is fast to run
locally while changing row markup and points straight at the helper.
It's still an outerloop test, so it doesn't gate PR CI.

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
  - [x] No

**🤖 Posted by AspireAgent (AI Agent)**

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 01d9682)
Mitch Denny (mitchdenny) pushed a commit that referenced this pull request Nov 10, 2026
## Description

The resource list's **Compact** density lost its one-line rows in
#21727. That PR removed the compact branch of the row markup and
switched `.density-compact .resource-row-text` to `flex-direction:
column`, so compact rows became the same two-line rows as Comfortable
("● Running · 1 minute ago"), only shorter. They then also picked up the
"No logs" / "Not applicable" badges added in #21819. #21727 also dropped
**Spacious**'s third detail line. That left the three densities
differing only in row height and font size, although
`ResourceListDensity` still documents one, two and three lines per
resource.

This PR restores the documented densities:

- **Compact**: one line per resource. The icon and name are followed by
aligned type and state-dot columns. In the sidebar, a row's actions
replace the columns while it's hovered or focused, or while its menu is
open, as before #21727. The expand toggle of parent rows stays visible,
and touch screens (`hover: none`) always show the actions. Availability
badges don't fit on one line: an unavailable row is muted, and the
reason stays in its tooltip and accessible description. Unviewed-error
badges sit just before the columns, so the columns stay aligned. A badge
keeps the same place while the row's actions show, so it never moves out
from under the pointer: it takes whichever place is further from the
row's end, clear of the columns or clear of the actions.
- **Comfortable**: unchanged.
- **Spacious**: sidebar rows have a third line again. It shows an
unhealthy health status if there is one, otherwise the primary endpoint,
otherwise the source. When no endpoint can be opened as a link, such as
a `tcp://` endpoint, the first endpoint is used, as in the expanded
list's endpoint column. Query values are masked. The endpoint and source
can take keyboard focus, which shows the full value in a tooltip and
gives it an accessible label.

In the expanded resource list (full canvas), compact rows use the same
single-line identity. From 960px wide, the identity column can grow by
the width of the type and state columns, so names aren't squeezed. That
extra width only raises the column's maximum, never its minimum, so rows
don't overflow the list at narrower widths. Actions there stay visible
all the time.

Persistent types such as "RabbitMQ (persistent)" widen every row's type
column together, so the columns stay aligned. The column is 128px where
rows can be hovered. On touch screens, where the action rail is always
showing, it stays at 64px at the default 420px pane width so names keep
their room.

### Screenshots / Recordings

The resource pane at its default 420px width, using the TestShop
playground, before (main) and after:

- **First row (before):** all three densities are the same two-line row.
- **Second row (after):** Compact is one line with the actions hidden at
rest, Comfortable is unchanged, and Spacious has its third line.
- **Third row:** compact on Structured logs. Before, rows had "No logs"
badges. After, those rows are muted instead. The last panel adds two
unviewed-error badges, injected into the page because the sample app
logs no errors.

![Before and after of the resource list in the Compact, Comfortable and
Spacious densities, plus compact rows on Structured
logs](https://github.com/user-attachments/assets/d31996b1-5001-4f8e-9605-8c2b3ae9601f)

Compact rows with an unviewed error, at rest and hovered. On main, the
badge jumped about 62px toward the actions when a row was hovered, with
a persistent type in the list. It now keeps its place:

![Compact rows with an unviewed-error badge at rest and hovered: before,
the badge jumps toward the actions; after, it keeps its place, with and
without a persistent type in the
list](https://github.com/user-attachments/assets/b88ffc0c-fbb0-4cca-b4dd-53e1ccd2c578)

### Validation

- Updated the `ResourcesLayoutTests` that asserted the two-line compact
row (`CompactDensity_ShowsOneLineWithTypeAndStateColumns`,
`WideRowMetadata_PreservesRuntimeStateAndAccessibleHealthWithRelativeStart`,
`AppHost_ContainerLifetimeQualifiesTypeAndBadgesResourceGlyph`). Added
`ResourceRowAvailabilityTests.CompactDensity_UnavailableRowKeepsReasonWithoutBadge`
and
`ResourcesLayoutTests.SpaciousSidebarDetail_PrefersUnhealthyStatusThenEndpointThenSource`.
- The 264 tests in `ResourcesLayoutTests`,
`ResourceRowAvailabilityTests`, `MainLayoutTests`,
`DashboardViewPreferencesTests` and `SettingsDialogTests` pass.
- Checked manually against the TestShop playground with Playwright at
the default 420px pane width:
- In every density, no row element overlaps another or spills out of its
row.
- Compact: the rest state, hover on a row and on a parent row, keyboard
focus, an open "…" menu, an error badge (simulated) and touch emulation.
  - The expanded list in every density.
- Updated `ResourcePaneResponsiveTests` for the restored geometry:
- 32px compact rows, and 22px, 32px and 40px icons for Compact,
Comfortable and Spacious.
  - Spacious rows are 76px in the sidebar and 60px in the expanded list.
- Compact sidebar rows show only the expand-toggle column at rest, and
the full action rail on hover or keyboard focus.
- A new horizontal-overflow check, with 960px and 990px stages. With the
previous track minimums it fails at 960px (980px of content).
- Added
`ResourcePaneResponsiveTests.CompactSidebar_ErrorBadgeKeepsItsPlaceWhileActionsShow`.
It seeds error logs and checks five states: at rest, the row hovered,
the badge hovered, the row focused, and the menu open. In each, the
badge must not move or be covered, must line up with its spacer, and
must not overlap a column or an action. It runs with and without a
persistent type in the list, and fails against the previous styles.
- 75 of the 80 `ResourcePaneResponsiveTests` pass locally. 4 failures
come from
`RowComposition_AlignsStatusAndSingleLineDetailsWithAccessibleContrast`
and `Accessibility_PaneAndPopoversHaveNoBlockingWcagViolations` at
390px, which fail the same way on main.
`Resizer_DoubleClickRestoresMinimumAcrossPresentationAndNavigation`
failed once in the full run because a drag didn't take effect, and
passed when rerun on its own.

Fixes # (issue)

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
    - If yes, did you have an API Review for it?
      - [ ] Yes
      - [ ] No
- Did you add `<remarks />` and `<code />` elements on your triple slash
comments?
      - [ ] Yes
      - [ ] No
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
    - If yes, have you done a threat model and had a security review?
      - [ ] Yes
      - [ ] No
  - [x] No

---------

Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com>
(cherry picked from commit db27327)
Mitch Denny (mitchdenny) pushed a commit that referenced this pull request Nov 10, 2026
…21924)

## Description

The scheduled [Outerloop Tests run on
main](https://github.com/microsoft/aspire/actions/runs/42437120205)
failed in two jobs after #21880:

- [Templates
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013):
all 10 `StarterTemplateWithRedisCacheTests` and
`StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for
`.resource-row-sub > .state-dot + span` in
`DashboardResourceAssertions.CheckResourcesAsync`.
- [Hosting.Testing
(ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062):
`DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
couldn't find the text "Dashboard test initial" in the resource row. The
row itself was there.

## Failure stats (14d, attempt 1)

`Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25
06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1
jobs that ran (success or failure). Hits: the failed job's log matches
the signature. Jobs that lost their runner, which have no log, are
excluded.

| | |
|---|---|
| Signature | Templates: `.resource-row-sub > .state-dot + span`
timeout; Hosting.Testing: `Dashboard test initial` not found |
| Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing
(ubuntu-latest), main |
| main pushes: hits / runs | 2 / 28 (7.14%) |
| main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on
2026-11-08. Templates also failed on main every scheduled run from
2026-10-10 to 2026-11-04 with other causes |
| PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) |
| Combined rate | 2 / 28 = **7.14%** |
| Flake check | main: failed on all 4 attempts of the run (not a flake)
|
| First / last hit | 2026-11-08 / 2026-11-08 |
| Still hitting main? | Hit on the latest scheduled main run
(2026-11-08); no main run since |
| Fix evidence | Local: default-density helper tests fail with the old
helper and pass with the new one. Outerloop dispatched on this PR's
head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. |

Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits):

| Date (UTC) | Branch | Job | Signature excerpt |
|---|---|---|---|
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928)
| **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard
test initial", Exact) element(s) not found` |
| [2026-11-08
02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061)
| **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot
+ span timeout` |

**Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled
main run failed both jobs on **all 4 attempts** with these signatures.
The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the
signature never appeared earlier in the window. Earlier Templates
failures (2026-10-27 to 2026-11-04) had other causes.

<!-- /failure-stats -->

**Why:** #21880 deliberately brought back single-line rows for
**Compact**, the dashboard's default density. A compact row shows the
state as a dot in a `.resource-row-meta-state` column, and the state's
name is in that column's `data-tooltip`. There's no `.resource-row-sub`
and no visible state text. These browser tests open the dashboard at its
default density, so they no longer found the state.
`Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase`
pins those tests to Comfortable unless they opt into the default. This
is a deterministic break, not a flake: the previous scheduled runs on
main
([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779),
before #21880) passed, and the only dashboard change in between is
#21880.

**Fix:** the tests now match the intended markup, and no assertions are
dropped:

- `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at
any density. In compact rows it reads the state column's tooltip. In
other densities it reads the text after the dot. `RowState(row, state)`
finds the element that shows a given state.
- `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while
a row hasn't rendered a state yet.
- `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint`
waits for the initial and then the updated state with `RowState`.
-
`TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards`
now runs at both the default (compact) density and Comfortable, and
checks `RowState` directly, so the helper is covered against the real
dashboard at the density the template tests use.

## Confidence

- Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old
helper, the two new default-density cases fail with the same `Timeout
... exceeded` as the Templates job.
- [Outerloop Tests dispatched on this
branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to
cover the Templates and Hosting.Testing jobs end to end. I didn't run
those two locally; they need the full template build and container
runtime.

**How we stop this recurring:** the shared helper's own test now runs at
the dashboard's default density, so a markup change to default rows
fails that test against the dashboard alone, which is fast to run
locally while changing row markup and points straight at the helper.
It's still an outerloop test, so it doesn't gate PR CI.

## Checklist

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [ ] Yes
  - [x] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
  - [x] No

**🤖 Posted by AspireAgent (AI Agent)**

Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
(cherry picked from commit 01d9682)
Sébastien Ros (sebastienros) added a commit that referenced this pull request Nov 11, 2026
Addresses the review feedback from #21412 (Modernize the dashboard
experience) that was deferred to the tracking issue #21421. Each item
was checked against the current code first. Item 17 was already fixed,
and every other item is addressed here. The design questions are
answered in a comment below.

Fixes #21421

| Items | Change |
|---|---|
| 1, 3, 6 | Replaces `WithDashboardPosition` with `WithDisplayOrder`,
backed by its own `DisplayOrderAnnotation` and transported as
`resource.displayOrder`. Like `ResourceUrlAnnotation.DisplayOrder`, it's
a relative sort key: higher values appear first and resources without
one count as 0. The API isn't in a release yet. Polyglot snapshots and
AppHosts are updated. |
| 2 | Keeps traits as resource properties, as decided in the review, and
corrects the comment that described them. |
| 4 | Moves tag validation out of the `TraitAnnotation` constructor into
`WithTrait`, which `WithTags` goes through. An `int` parameter covers
display order validation. |
| 5 | Replaces `unresolved_parameter_names` with a generic
`related_resource_names` on the notification message, keeping field
number 5. The dashboard offers to show the related resources in place of
the primary button, and the parameter form still accepts the
notification. |
| 7 | Restores the `CircularBuffer` removal checks under `#if DEBUG`,
for buffers up to 1,024 items, so large console log buffers don't bring
back the CPU cost. |
| 8, 20 | Recordings don't mount a live terminal. The missing-parameters
form is read-only for recordings and doesn't accept the live AppHost's
prompt. |
| 9 | `ParameterOverview` and `MissingParametersPanel` edit their own
copy of the shared command inputs. |
| 10 | Keeps generated input names when a prompt has a dynamically
loaded input, because callbacks can read `AllInputs` regardless of
`DependsOnInputs`. Names that aren't unique are still renamed, because
the prompt can't be shown otherwise, and every other input keeps its
name. Each prompt input also carries the parameter it is for
(`InteractionInput.related_resource_name`), and the parameter form
matches inputs by that field rather than by name. A generated name can
then match another, already resolved parameter without the form editing
the wrong input. |
| 11, 19 | The inline form forwards `UpdateStateOnChange`. Without an
active interaction, its file button runs the set-parameter command. |
| 12 | Sums delta counter observations per bucket. Repeated delta
numbers are no longer merged at ingestion, because that lost their
times, and the SQLite rollup now sums them by observation time instead
of keeping the largest, without overflowing. Delta numbers are selected
by their end, so one that arrives out of order isn't lost. Rollups and
chart totals stay finite without depending on the order of the values,
so a sum too large for a double no longer stops the chart from
rendering, and chart intervals compare times in nanoseconds like the
rollup query. Cloning metric values keeps their count, and delta counter
cards show the latest change. |
| 13 | Preserves negative levels. The main metrics chart shares this
calculation, so it's corrected too. |
| 14, 15 | A recording's activity window ends at its last request, or
its last error log if it has no requests, and has its own labels. In the
summary layout, the live window now refreshes every 30 seconds. |
| 16 | Scopes the overview's pending refresh flag to the selection, and
queues a refresh after console filter changes cancel one. Adds an
internal `TelemetryRefreshDelayAsync` seam so a test can hold a refresh
open. |
| 18 | Ctrl-click after "select all" no longer adds a resource kind
that's switched off. |
| 21, 22 | Display orders come from the requesting run rather than the
store, and moves use the displayed tie-break. |
| 23 | Localizes the trace back button. `main` has since localized the
activity and insights strings, so this PR only adds the
recording-specific ones. |

- `Aspire.Dashboard.Tests`: 2,655 passed
- `Aspire.Dashboard.Components.Tests`: 1,384 passed, after merging
`main`
- `Aspire.Hosting.Tests`, limited to the trait, parameter processor,
dashboard service and interaction service test classes: 257 passed
- Code generation tests for TypeScript, Python, Go, Java and Rust: 412
passed, before the last review fixes, which didn't change exported API
- `TestShop.AppHost` builds

The regression tests for items 14, 15 and 16 were checked by temporarily
undoing each fix and confirming they failed.

Not covered: the polyglot AppHosts were renamed but not run, and nothing
was checked in a browser.

- Is this feature complete?
  - [x] Yes. Ready to ship.
  - [ ] No. Follow-up changes expected.
- Are you including unit tests for the changes and scenario tests if
relevant?
  - [x] Yes
  - [ ] No
- Did you add public API?
  - [x] Yes
    - If yes, did you have an API Review for it?
      - [ ] Yes
- [x] No. Not needed for `WithDisplayOrder` and
`DisplayOrderAnnotation`, as decided in the review.
- Did you add `<remarks />` and `<code />` elements on your triple slash
comments?
      - [ ] Yes
      - [x] No
  - [ ] No
- Does the change make any security assumptions or guarantees?
  - [ ] Yes
    - If yes, have you done a threat model and had a security review?
      - [ ] Yes
      - [ ] No
  - [x] No

---------

Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com>
(cherry picked from commit 727eaa7)

Adapted for release/17.0, which doesn't have the dashboard changes this
built on in main (#21840, #21861, #21880, #21881, #21943):
- Tags keep release/17.0's Aspire_Dashboard_Tag name
  (KnownResourceTraits.DashboardTag); the Aspire_Tag rename is #21881.
- ResourceOrderStore keeps its SQLite storage. The AppHost display order
  semantics, GetPositions(resources) and TryMove(..., resources) are ported
  onto it, and the store no longer keeps AppHost positions itself.
- ResourceOrderStoreTests: the four new display order tests run against the
  SQLite store, with a new store instance standing in for a restart.
- Tests for code that isn't on release/17.0 aren't brought over
  (ParameterFormReplacedAndPromptShownAgain, Standalone_RemovedTelemetrySource).
- Resources.Designer.cs and the Go TwoPassScanningGeneratedAspire snapshot
  are regenerated against release/17.0.
Jose Perez Rodriguez (joperezr) added a commit that referenced this pull request Nov 12, 2026
Combines the stacked dashboard backports into one PR for `release/17.0`.
The commits are cherry-picked in stack order, one per source PR, onto
the current `release/17.0` tip:

1. #22125: Store dashboard tags,
resource order and metric pins in browser local storage (#21840)
2. #22126: Add dashboard
resource-selector dropdown (#21861)
3. #22127: Rename the WithTags
trait from Aspire_Dashboard_Tag to Aspire_Tag (#21881)
4. #22128: Restore single-line
compact rows in the dashboard resource list (#21880)
5. #22129: Allocate the next
dashboard run ID when two dashboards start in the same millisecond
(#21913)
6. #22130: Simplify the
dashboard around its two main jobs (#21943)
7. #22131: Return 400 for OTLP
JSON requests with malformed ids (#21436)
8. #22132: Address deferred
review feedback for the modern dashboard (#21766)

## Conflict resolution

The stack was cut from an older `release/17.0`. Since then, #22123
(Harden Go SDK runtime) landed and regenerated
`TwoPassScanningGeneratedAspire.verified.go`. That conflicted with
#21766, which replaces `WithDashboardPosition` with `WithDisplayOrder`.
The resolution drops `WithDashboardPosition` and regenerates the
snapshot with the current Go generator, so `WithDisplayOrder` now uses
the hardened `s.Err()` pattern. The other seven commits applied cleanly.

## Validation

All `Aspire.Hosting.CodeGeneration.*.Tests` projects pass locally (Go,
Java, Python, Rust, TypeScript).

---------

Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: James Newton-King <james@newtonking.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: David Fowler <davidfowl@gmail.com>
Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com>
Co-authored-by: Maddy Montaquila <maddyleger1@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Maddy Montaquila <maddy@MadBook-Pro-20.local>
Co-authored-by: Taha Koçal <tahakocalgs@gmail.com>
Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants