Overview
PR #313 delivered the core popover redesign (issue #294) and passes all CI gates. However, a strict comparison of the final design spec in #178 comment 4405046489 and #178 comment 4405047942 against the merged diff reveals four residual gaps.
Gap 1 — Runner rows not tappable (Phase 6 / #310)
Spec says: Every row with a > chevron is navigable — the final ASCII art shows > on runner rows:
| ● eons-MacBookPro-2 CPU: 38.2% MEM: 4.1% > |
PR does: PopoverLocalRunnerRow renders Image(systemName: "chevron.right") but wraps nothing in a Button — the chevron is cosmetic only and the row is inert.
Fix required:
- Wrap each runner row in a
Button that navigates to a runner detail view (or at minimum calls an onSelectRunner callback wired in AppDelegate).
- Follow the same
navigate(to:) pattern used by onSelectAction and onSelectJob.
- If a full runner detail view is out of scope, remove the
chevron.right from the row until navigation is wired — showing a non-functional affordance is worse than showing none.
Related: #310
Gap 2 — ↳ inline job rows are silently tappable (Phase 3 spec violation)
Spec says (final design comment #4405047942):
↳ child rows have no > since they're not independently tappable
PR does: InlineJobRowsView.jobRow is correctly missing the > icon ✅, but each row is still wrapped in:
Button(action: { onSelectJob(job) }, label: { jobRow(job) })
.buttonStyle(.plain)
This makes the rows silently tappable (navigation fires on click) with no visual indication — violating the spec's intent that inline job rows are read-only/passive context.
Fix required:
- Remove the
Button wrapper from InlineJobRowsView.body.
- Render job rows as plain
HStack / VStack with no tap action.
- If a future phase adds job drill-down from inline rows, reintroduce the
Button with the > chevron at that point.
Gap 3 — System stats header missing progress bar characters
Spec says (both final ASCII art blocks):
| CPU ██░ 24.6% MEM ██░ 6.8/16GB DISK ███ 342/460GB [⚙] [×] |
Each stat has a short block-character bar (██░, ███░) before the numeric value, providing instant at-a-glance fill feedback.
PR does: statChip renders label + value only — no bar characters:
statChip(label: "CPU", value: String(format: "%.1f%%", stats.cpuPct), pct: stats.cpuPct)
Fix required:
- Add a compact 3-character block bar before each value using Unicode block characters (
█, ░) scaled to pct.
- Suggested helper (pure, no new dependencies):
private func blockBar(pct: Double, width: Int = 3) -> String {
let filled = Int((pct / 100.0 * Double(width)).rounded())
return String(repeating: "█", count: filled) + String(repeating: "░", count: width - filled)
}
Gap 4 — + N more… inline jobs caption is passive, not a tappable "Load more" button
PR description says: "expanded groups with > 4 jobs show a tappable "Load more jobs…" button (reveals +4 at a time)"
PR diff does: Renders a passive Text("+ \(activeJobs.count - cap) more…") with no tap action:
if activeJobs.count > cap {
Text("+ \(activeJobs.count - cap) more…")
.font(.caption2).foregroundColor(.secondary)
...
}
This means users can never reveal the hidden inline jobs — they're silently truncated.
Fix required:
- Add
@State private var inlineJobsLimit: [String: Int] (keyed by group.id) in PopoverMainView (or pass a binding into InlineJobRowsView).
- Replace the passive caption with a tappable button that increments the per-group cap:
Button(action: { inlineJobsLimit[group.id, default: 4] += 4 }) {
Text("+ \(activeJobs.count - cap) more jobs…")
.font(.caption2).foregroundColor(.accentColor)
}
.buttonStyle(.plain)
- Initial cap: 4. Each tap reveals +4 more.
Acceptance Criteria
References
Overview
PR #313 delivered the core popover redesign (issue #294) and passes all CI gates. However, a strict comparison of the final design spec in #178 comment 4405046489 and #178 comment 4405047942 against the merged diff reveals four residual gaps.
Gap 1 — Runner rows not tappable (Phase 6 / #310)
Spec says: Every row with a
>chevron is navigable — the final ASCII art shows>on runner rows:PR does:
PopoverLocalRunnerRowrendersImage(systemName: "chevron.right")but wraps nothing in aButton— the chevron is cosmetic only and the row is inert.Fix required:
Buttonthat navigates to a runner detail view (or at minimum calls anonSelectRunnercallback wired inAppDelegate).navigate(to:)pattern used byonSelectActionandonSelectJob.chevron.rightfrom the row until navigation is wired — showing a non-functional affordance is worse than showing none.Related: #310
Gap 2 —
↳inline job rows are silently tappable (Phase 3 spec violation)Spec says (final design comment #4405047942):
PR does:
InlineJobRowsView.jobRowis correctly missing the>icon ✅, but each row is still wrapped in:This makes the rows silently tappable (navigation fires on click) with no visual indication — violating the spec's intent that inline job rows are read-only/passive context.
Fix required:
Buttonwrapper fromInlineJobRowsView.body.HStack/VStackwith no tap action.Buttonwith the>chevron at that point.Gap 3 — System stats header missing progress bar characters
Spec says (both final ASCII art blocks):
Each stat has a short block-character bar (
██░,███░) before the numeric value, providing instant at-a-glance fill feedback.PR does:
statChiprenders label + value only — no bar characters:Fix required:
█,░) scaled topct."CPU \(blockBar(pct: stats.cpuPct)) \(String(format: "%.1f%%", stats.cpuPct))".lineLimit(1)on the chip text — load-bearing per regression guard (ref ⚠️ Regression guard: rules for editing PopoverMainView, AppDelegate & ActiveJob #52 🚨 DO NOT REPEAT: Definitive regression anti-pattern guide (read before every edit) #54).Gap 4 —
+ N more…inline jobs caption is passive, not a tappable "Load more" buttonPR description says: "expanded groups with > 4 jobs show a tappable
"Load more jobs…"button (reveals +4 at a time)"PR diff does: Renders a passive
Text("+ \(activeJobs.count - cap) more…")with no tap action:This means users can never reveal the hidden inline jobs — they're silently truncated.
Fix required:
@State private var inlineJobsLimit: [String: Int](keyed bygroup.id) inPopoverMainView(or pass a binding intoInlineJobRowsView).Acceptance Criteria
>chevron is removed until navigation exists)↳inline job rows have noButtonwrapper and are not tappable██░ 24.6%)References