You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a sibling 4.0 workstream to #391, not a follow-up release. The migration design (docs/superpowers/specs/2026-08-10-xcresulttool-legacy-migration-design.md) already concedes the point: the report's visual contract breaks in 4.0 either way once activityType loses its data source — the five activity-type states stop being renderable no matter what the templates look like. Breaking the visual contract once, in the same major release, is cheaper than breaking it twice.
Sequencing (strict)
Templates stay frozen until Migrate off xcresulttool --legacy before Apple removes it #391's legacy/modern differential passes. The differential attributes every rendered diff to the reader swap; if reader and markup change together it can no longer attribute anything. No template PR merges before that gate.
Design exploration is not blocked. Palette, type, layout, dark-mode strategy can all be worked out concurrently on the current markup — only the merge is serialized behind the differential.
HTMLTemplates.swift claims to be generated, but the generator was deleted in #295 #434 must be resolved before any markup is written.Sources/XCTestHTMLReportCore/Classes/HTMLTemplates.swift is the real, hand-maintained source despite its stale "autogenerated — DO NOT EDIT" header; Sources/XCTestHTMLReportCore/HTML/*.html is a dead copy excluded from the build and 28 diff hunks behind. Anyone who starts the redesign from HTML/ is editing markup that does not ship. Whichever of HTMLTemplates.swift claims to be generated, but the generator was deleted in #295 #434's options is chosen, it has to land first so the redesign has exactly one source of truth (and one the linters actually see).
The structural problem
The stylesheet is 585 lines with zero custom properties, zero @media queries, no dark mode, no grid; all 16 colours are hardcoded hex literals (plus bare white/red), the font stack pins "SF Pro Display" with no system-ui and no generic fallback, and sizing is 99 fixed-px values with a single em in the whole sheet. There is no token layer, so every visual refresh is a scattered find-and-replace across literals — which is why there has been none since 2020.
A 2026-08-12 visual audit (rendered from the three fixture bundles next to Xcode 26's report on the same TestResults.xcresult) confirmed all of the above empirically — with the OS in dark mode, the report renders identical light — and catalogued the user-facing consequences:
No run summary of any kind: no counts visualization, no failure digest, no run date/duration/OS headline. The overview is just the tree. Xcode leads with a donut, per-device bars, a failure digest with messages, and build/runtime warnings.
A fixed three-pane layout spends ~200px on a device sidebar (even for single-device runs) and ~28% of the window on a right pane that almost always says "No Selected Attachment".
No <meta viewport> and fixed-px panes make small screens unusable: at 375px the sidebar eats over half the viewport and test rows clip mid-word.
Mixed-outcome (1 failed, 1 succeeded) and unknown-state tests render a blank status cell — only pass/fail/skip have icons.
Raw internals leak into the UI (kXCTAttachmentScreenRecording as an attachment label); the selected video keeps playing in the right pane after switching to the Logs tab.
The default linking-mode output silently breaks if moved anywhere: markup references TestResults.xcresult/<file>, so a report rendered with -o <elsewhere> has every attachment 404 with no warning — and the export mutates the source bundle. Inline mode (-i) is the true single-file artifact, at 8.6MB for the 54-second sample run.
Still worth protecting: the single-artifact shareability (our reason to exist), whole-run-in-one-tree visibility (Xcode hides passed tests behind filter dropdowns), compact retry/iteration rendering, JUnit export, instant client-side filters.
expectedFailure is now a named status in the ParsedResult model, though the renderer still flattens it — the redesign can surface it.
Swift Testing parameterized-test arguments exist in the port (ParsedTestCase.arguments, empty on legacy); the current templates have no slot for them.
Per-activity durations and the five activityType states are gone — activities carry start ordering and isFailure only. Xcode's own report shows elapsed offsets rather than per-activity durations, so this is a design opportunity, not just a loss.
Non-goal
This issue deliberately does not propose a visual direction (palette, type, layout, dark-mode approach). That exploration is unstarted on purpose and is the maintainer's call.
Direction decision (maintainer, 2026-08-13): Option C — conservative refresh — lands first; the A-vs-B choice (Xcode-native vs neutral-modern full redesign) is deliberately deferred until C's token layer exists, since C is a strict subset of both. Decision package with the three rendered mockups, tradeoffs, and QA screenshots is preserved locally (ui-recon-track3/direction-package/). C's scope, from the package: token layer, dark mode, viewport meta, system-ui font stack with fallback, WCAG contrast fixes, responsive collapse (sidebar→strip, attachment pane→bottom sheet), SVG icons replacing ~300KB base64 PNG, status icons for mixed/unknown/expectedFailure, type-derived attachment labels (required — the modern backend has no user-supplied names), tab title carrying run identity. Prerequisites sequenced: #434 resolution PR (in flight), then tokenization, then the refresh; sample-app fixture coverage for expectedFailure/PNG-attachment cases runs in parallel since nothing generated exercises them today.
Option C is delivered: #452 (source of truth), #455 (token layer), #456 (theme: dark mode, contrast, viewport, system fonts, run-identity titles), #459 (structure: responsive collapse, SVG icons −79% payload, six-state status icons, kXCT*-free attachment labels) — all merged 2026-08-13. The conservative refresh is complete; this issue stays open only for the deferred A-vs-B decision (full redesign direction), for which the mockups and decision doc remain preserved locally (ui-recon-track3/direction-package/). Related follow-ups: the expected-failure filter question (see newly filed issue), #446, #454.
Activity
tylervick commentedon Aug 13, 2026
Direction decision (maintainer, 2026-08-13): Option C — conservative refresh — lands first; the A-vs-B choice (Xcode-native vs neutral-modern full redesign) is deliberately deferred until C's token layer exists, since C is a strict subset of both. Decision package with the three rendered mockups, tradeoffs, and QA screenshots is preserved locally (ui-recon-track3/direction-package/). C's scope, from the package: token layer, dark mode, viewport meta, system-ui font stack with fallback, WCAG contrast fixes, responsive collapse (sidebar→strip, attachment pane→bottom sheet), SVG icons replacing ~300KB base64 PNG, status icons for mixed/unknown/expectedFailure, type-derived attachment labels (required — the modern backend has no user-supplied names), tab title carrying run identity. Prerequisites sequenced: #434 resolution PR (in flight), then tokenization, then the refresh; sample-app fixture coverage for expectedFailure/PNG-attachment cases runs in parallel since nothing generated exercises them today.
Fixture coverage for the redesign: expectedFailure + PNG attachment (…
Tokenize the stylesheet: 33 CSS custom properties, zero visual change…
C-refresh part 1/2 — theme: dark mode, WCAG floors, system font, run-…
Propagate expectedFailure through the suite-row aggregate (refs #439)
Fix the two review findings on the C-refresh structure half (refs #439)
C-refresh part 2/2 — structure: responsive collapse, SVG icons, all s…
tylervick commentedon Aug 13, 2026
Option C is delivered: #452 (source of truth), #455 (token layer), #456 (theme: dark mode, contrast, viewport, system fonts, run-identity titles), #459 (structure: responsive collapse, SVG icons −79% payload, six-state status icons, kXCT*-free attachment labels) — all merged 2026-08-13. The conservative refresh is complete; this issue stays open only for the deferred A-vs-B decision (full redesign direction), for which the mockups and decision doc remain preserved locally (ui-recon-track3/direction-package/). Related follow-ups: the expected-failure filter question (see newly filed issue), #446, #454.
Fix effectiveBackground() to skip masked icon fills
Design: test coverage for the rendered report
Fix effectiveBackground() to skip masked icon fills
Run the browser assertions in CI (#461)
Publish a permanent demo render for every stable release under /v/ (#464
20 remaining items
Make the row bleed actually paint, and the axe gate actually bite (refs
Make the tests tree read like Xcode's outline (refs #439) (#483)
Fix the three behaviour findings from the A3a review (refs #439)
Give the placeholder-order pins their own file (refs #439)
Close a hole in the 375px glyph assertion (refs #439)
Point the substitution-order comment at the test that holds it (refs #…
Scope the placeholder precondition to the span it lands in (refs #439)
Give each view its own surface and the report one device picker (refs #…
Match suite names in the tree filter too
Put the log's monospace stack in the token layer
Carry every tail screenshot with its row, not just the nearest
Keep the executions count level with the rows on screen
Say only what the fixtures show about the combined shape
Give every status a filter, and the log something to filter it with (f…
Tell the release notes the redesign has landed (refs #439) (#488)