cmux NEXT feed: the Labs reports basket is AppKit (No new SwiftUI)
The Labs reports basket (CmuxNextFeed/Views/ReportsBasketView.swift, ReportsBasketRow.swift, ReportsBasketDetail.swift) was new SwiftUI outside the no-SwiftUI allowlist, so "No new SwiftUI" failed on feat-cmux-next (base run 40146316318). The design rule is: "New macOS UI is AppKit (NSView, NSStackView, NSTableView, NSCollectionView, CALayer) built from CmuxNextDesign" (plans/cmux-next/no-swiftui-DESIGN.md, rule 1). This PR ports the basket to AppKit instead of adding allowlist entries. Paired with #25021, which fixes the other six findings, the check reports 0.
Who introduced it: a Leo lane added the basket in #24616. lawrencecchen later fixed its reply path (66a9ebb8, 77c2da43; e540d21d). Those reply semantics are carried over line for line.
What changed
ReportsBasketView,ReportsBasketRowandReportsBasketDetailareNSViews built fromNSStackView/NSScrollView/NSTextField/NSButton, withCmuxNextDesign'sTypeScaleand the panel's theme-resolved colors.ReportsBasketColorsholds the AppKit form, resolved insideFeedColors.resolvedin the host's theme scope, plus a hairline that respectsappearance.borders.- The panel itself is still SwiftUI (Feed is wave 1 item 8), so one
NSViewRepresentable(ReportsBasketHost) in the already-allowlistedFeedView.swiftmounts the basket. No allowlist line is added. - Behaviour is unchanged. The newest report is selected on appear, and a click selects another report and empties the draft. Each report has at most one reply in flight, and the button waits. A refused reply keeps its draft, shows "Not sent", and is resent with the same
promptID(ReportReply(session:text:retrying:)). A sent reply clears the draft only if the draft is unchanged and the report is still selected. The strings and keys, the list width (340-420, ideal 380), the detail's 420 minimum, the time and long date formats, and the paddings all match the SwiftUI version.
Observation, and when it stops: model.reports is read with withObservationTracking. After each change the view re-arms, but only while it is in a window. Out of a window, the pending registration fires once at the next reports change and is not renewed. Deinit leaves only a weak-self closure, dropped at that same change. On re-entry a pending registration is reused, so one view never holds two. withObservationTracking has no cancel, and the cancellable Observations needs macOS 26 while the app supports 14, so this is the same lifetime as the package's other uses (AgentChatHeaderWatch, AppBrowserHost, TerminalClipboardReadService). The PR adds no timers. There are two Tasks: the re-arm, which ends at once, and one per reply send, which ends when the host answers within its deadline. Each has a task-owner note.
Tests (rule 4: red commit 41b2de9f, then the port): Tests/CmuxNextAppTests/ReportsBasketViewTests.swift checks: the basket builds no SwiftUI view; the list and detail fill the basket (list column 340-420, detail at least 420); a long report wraps inside its panes. The newest report is selected and a press selects another. A report that arrives gets a row without taking the selection. A reply is sent once while in flight, and a refused one keeps its draft and its id, then clears on success. They run in this PR's cmux-next swift test job, and the package compile also runs on the build fleet (cmux-ci compile-check --package CmuxNext).
Screenshots: none, for these reasons. The fleet capture (cmux-ci capture) has no Feed or Inbox scene, and its debug.window_snapshot captures only the main window, while the Feed panel is its own window. The basket's rows come from the acpmux reports mailbox, which a capture slot cannot seed ("daemon steps need the tour runner's session; not supported in a capture slot"), so a capture could show only the empty state. Instead, the layout keeps every metric of the SwiftUI version listed above, and the Labs basket stays behind the Debug feed.layout = reportsBasket switch.
Review: Codex gpt-6-astra at xhigh, read-only, five rounds. Round 1 blocked on stack distribution, scroll widths, reply-field growth and stacked observations (fixed in 3ba153a3). Round 2 blocked on label widths (fixed with a wrap test). Round 3 shipped at 7f771300. CI then flagged an IUO, two super selector overrides and a fourth top-level type. Those were fixed: the reply field now overrides nothing, and the detail sizes it. Round 5: SHIP at 35f75f1c. Fleet compile of CmuxNext passed at that head (cmux-ci compile-check, 222 s). In CI, the six basket tests are neither red nor unfinished. The remaining reds are not this PR's: No new SwiftUI and Checks job structure are base reds fixed by #25021, and the swift-test reds are in sidebar, split, zoom and home suites the basket doesn't touch. PR capture is explained above.
Risk: low to medium, Labs only. The basket shows only with Debug Settings > Feed > Layout = Reports basket. A layout difference (for example the reply field's growth to four lines) is cosmetic. The reply state machine is the risky part and the tests cover it.
š¤ Generated with Claude Code
Agent session: cosmic-gray