feat(backlog): progressive disclosure + session diagnostics for item detail panel - #208
Merged
Conversation
Adds Phase 3 (plan.md) and ADR-027 (Radix Accordion for the shared Collapsible primitive) to the existing requirements.md + research/ artifacts, plus stages the prior phases' outputs that hadn't been committed yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
Phase 3 UX design deliverable: wireframes (desktop + mobile), interaction flows, error/edge-case handling, and 24 testable UX acceptance criteria for the redesigned BacklogItemDetail panel, SessionDiagnosticPanel's 3 synthetic- session sub-states, and the BacklogItemCard blocker chip — consistent with implementation/plan.md's exact component names and behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…-detail-ux Architecture review, adversarial review, pre-mortem, and validation plan, plus plan.md/ADR-027/ux.md edits resolving the 4 blockers, 2 pre-mortem P1s, and triad-review UX gaps found along the way.
…le, currentWorkSession, sessionKind, BlockerChip Epic 1.1 of the backlog-item-detail-ux plan: the four reusable building blocks later epics depend on. - Collapsible.tsx: CollapsibleGroup + CollapsibleSection built on @radix-ui/react-accordion (ADR-027) — real <button aria-expanded> headers, collapsed content removed from the DOM, Home/End/Arrow roving-tabindex nav across sibling headers sharing one CollapsibleGroup. - useSectionExpandState: localStorage-backed per-item/per-section expand state, defensive try/catch per RecentFilesSection.tsx's precedent. - currentWorkSession.ts: single getLatestWorkSession()/useCurrentWorkSession() helper replacing 4 independent inline re-derivations in BacklogItemDetail.tsx (D3) that could previously drift out of sync. - sessionKind.ts: closed classifySessionKind() classifier, wired into the Sessions row — fixes the pre-existing dead-link bug where a manual-review-*/diff-error-* session fell through to a clickable <a href="/?session=..."> that was never Instance-backed. - BlockerChip.tsx: shared full/compact "waiting on X" indicator reusing stuckReason.ts's icon/label/duration formatting verbatim. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…ic 5.1) Give BacklogItemCard.tsx the same "waiting on X" signal the detail view's LifecycleSummary has and the same canonical status vocabulary the Stage Tracker uses: - Story 5.1.0: add a status label (getStatusLabel(item.status)) to the card header, distinct from and in addition to the existing action-button text (getActionSpec() is unchanged). - Story 5.1.1: wire useStuckBacklogItems() once at board/page.tsx level and thread the resolved StuckBacklogItem per item down through BacklogBoard to each BacklogItemCard, which renders the compact BlockerChip in its footer when the item is flagged stuck. cardFooter gains flex-wrap so the chip doesn't overflow on narrow widths. - Story 5.1.2: measured BacklogItemBadge.tsx's list-row width (260px max, single-line, already 3 packed inline elements) and decided to DEFER the compact BlockerChip there — no width budget for a 4th element without truncating the title further. Reasoning recorded in a code comment above the badge's status chip, with a regression test guarding the deferred decision. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
Add StageTracker, BlockerChip integration, and LivenessLine, composed into a single always-visible LifecycleSummary that replaces the old standalone status badge in BacklogItemDetail's header — the single authoritative place lifecycle status is shown (D1). - StageTracker: pure deriveStageDisplay(status) + 5-node stepper. queued/ pr_pending render as modifier badges (never a 6th node); refining folds into Idea; archived renders a dimmed neutral tracker with an "Archived" ribbon overlay rather than guessing the pre-archive stage. - LifecycleSummary: BlockerChip (full variant) renders only when useStuckBacklogItems() flags this item — the hook's own loading-starts- empty and error-retains-last-known contracts mean no special-casing is needed to satisfy "absent = not blocked" and "never a false all-clear." - LivenessLine: deriveLastActivity() picks the max timestamp across linked sessions, statusEvents, and progressNotes, falling back to item.createdAt. Deliberately plain static text (no aria-live) per design/ux.md, since re-announcing on every 5s poll tick would be noise, not help. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
Add key={selectedItemId} to BacklogItemDetail's call site in
backlog/page.tsx so switching items fully resets per-item UI state
(e.g. an open manual-review form) instead of leaking into the next
item. board/page.tsx reuses the same route/component and needed no
separate fix. Adds regression coverage proving the remount fires on
itemId change but not on a same-itemId poll-driven rerender.
Epic 3.1, Story 3.1.1 of project_plans/backlog-item-detail-ux.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…tory 3.1.2) Split BacklogItemDetail.tsx's Planning record, Reviewing (work-session context + GateVerdictBox), and Pull Request blocks into their own sibling components under components/backlog/detail/. Reviewing and Pull Request are Collapsible-wrapped, default-expanded only when the item is in the matching status; Planning stays always-visible (primary content). Also lands the D4 fix: VcsWidgetGithubRow/VcsWidget gain an opt-out showPrLink prop (default true) so PullRequestSection can be the single data source for PR URL text once VersionControlSection wires it up in Story 3.1.4. VcsPanel.tsx and UnfinishedItemDetail.tsx are untouched and keep the default true; regression tests added confirming their rendering is unaffected. Epic 3.1, Story 3.1.2 of project_plans/backlog-item-detail-ux. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…gaps (Story 3.1.3) Split DescriptionSection (Collapsible, collapsed by default) and ActionsSection (always-expanded, includes the manual-review form) out of BacklogItemDetail.tsx, preserving every action/manual-review data-testid verbatim. Extends the polling-suspend guard beyond editMode to also cover showManualReview and actionLoading !== null (pre-mortem P1 #4) — a poll firing while the manual-review form is open or an Approve/Override request is in flight could otherwise clobber unsaved input or unmount a section mid-request, risking a double-submit. Also relabels GateVerdictBox's per-criterion list ("Review outcome per criterion") to resolve D2 — it no longer reads as a second, competing acceptance-criteria checklist alongside AcCriteriaList. Epic 3.1, Story 3.1.3 of project_plans/backlog-item-detail-ux. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…badge, shared CollapsibleGroup (Story 3.1.4) Splits PlanArtifacts, VersionControl, Sessions, WorkflowHistory, ProgressHistory, and Notes out of BacklogItemDetail.tsx into their own Collapsible sibling components. Adds the shared, localStorage-backed useShowMore hook (Blocker C fix + pre-mortem finding #2 — the "show all" choice persists per item/section across re-opens, not a plain useState that re-collapses on every mount) and applies it to Sessions (cap 5), WorkflowHistory (cap 8), and ProgressHistory (cap 8). Extracts resolvePipelineModeDisplay() to lib/backlog/pipelineModeDisplay.ts so both SessionsSection and the new LifecycleSummary Pipeline badge (D6) share one implementation. Wraps every sibling CollapsibleSection (Reviewing/PullRequest from 3.1.2, Description from 3.1.3, and this story's six) in one shared CollapsibleGroup, with a controlled value/onValueChange backed by useSectionExpandState per section — this is what actually delivers ADR-027's cross-header Home/End/Arrow keyboard-nav justification. ActionsSection/PlanningSection stay outside the group as always- visible primary content; Actions is repositioned before the group (rather than its original position between Description and Plan Artifacts) so the group's Radix Root can be contiguous. Also lands Story 3.1.5's auto-expand-once guard: a status-dependent section's default only applies once, the first time an item's data loads, and never again on a later poll-driven status change — a one-time effect checks for an existing localStorage preference before applying the computed default so a prior visit's collapse choice is never clobbered. Adds beforeEach(() => localStorage.clear()) to BacklogItemDetail's test suites — the new per-section/show-more persistence otherwise leaks expand state across tests reusing the same itemId. Epic 3.1, Story 3.1.4 of project_plans/backlog-item-detail-ux. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…tus changes (Story 3.1.5) The initialExpandAppliedRef one-shot effect landed as part of Story 3.1.4's CollapsibleGroup wiring (each status-dependent section's default only applies once, right after an item's first successful load). This adds the three regression tests validation.md calls for directly against the composed BacklogItemDetail tree: - VersionControlSection auto-expands on first mount for an in_progress-status item - a user's manual collapse survives a same-itemId poll tick that returns a fresh item object - ReviewingSection's one-shot default does not retroactively fire when status transitions from idea to review mid-poll without an itemId/key change (the documented "known, intentional exception") Epic 3.1, Story 3.1.5 of project_plans/backlog-item-detail-ux. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…curityCheck Confirmed RunPreGateSecurityCheck's error string only ever embeds a fixed pattern-name label from secretPatterns, never the raw diff or matched secret substring, and that review_gate.go's Sprintf consumer does no further string surgery that could reintroduce it. Adds an automated regression test proving this end-to-end so Story 4.1.3's BlockedNotice can safely render reviewVerdict.summary verbatim. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
Story 4.1.2 (Structured Diagnostic renderer). Adds a readOnly prop to both components that omits their action-button rows (Apply/Skip/Refine and Approve/Reopen/Override/Skip Gate/Re-review respectively) from the DOM while preserving all informational content (summary, suggestions, task list, per-criterion outcomes) — the read-only historical-record presentation Epic 4.1's SessionDiagnosticPanel dispatches Headless Diagnostic Sessions to. TriageReviewPanel's readOnly mode also ignores any pre-existing localStorage dismissal for the item, since a headless diagnostic session's readOnly render shares the same dismissed-flag key as the live interactive panel for that item — a historical record should never be dismissible in the first place. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
Stories 4.1.2 (headless branch) and 4.1.3 (blocked/manual-review branches). SessionDiagnosticPanel routes a classified Synthetic Session to the correct read-only presentation: - headless_diagnostic with triageResult -> TriageReviewPanel readOnly - headless_diagnostic with reviewVerdict -> GateVerdictBox readOnly - headless_diagnostic with neither populated (malformed/partial data) -> BlockedNotice, so this edge case can't reproduce the original inert-row bug for a new case (architecture-review-flagged gap) - blocked_guardrail / manual_review_marker -> BlockedNotice BlockedNotice is the plain-text Blocked-Before-Start Notice (ux.md Surface 4 & 5): role="status", renders reviewVerdict.summary verbatim (safe per Story 4.1.1's security review) with a distinct icon/label per kind, falling back to "No summary recorded." / "No diagnostic data recorded." rather than an empty box. Neither surface offers an "open session" affordance -- there was never a session to open. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…ead session rows Story 4.1.4. Replaces the inert-span / dead-anchor row-kind branching with a classifySessionKind() switch: "work"/"review" rows keep their existing <a href="/?session=...">, and the 3 synthetic kinds now render as a Collapsible header expanding inline to SessionDiagnosticPanel (fixes the manual-review-*/diff-error-* dead-link bug the Story 1.1.3 classifier identified but Epic 3's mechanical swap left un-wired to a real renderer). Per-row Collapsibles get their own local, uncontrolled CollapsibleGroup rather than joining SessionsSection's ancestor page-level CollapsibleGroup (Task 3.1.4i): that outer group is a controlled Accordion.Root whose `value` only tracks the fixed top-level section-key set, so a row's ephemeral sectionKey would be immediately forced closed again by the controlled prop, and would incorrectly merge dozens of row headers into the page-level Home/End/Arrow nav loop ADR-027 scoped to top-level siblings only. Also drops the old always-visible reviewVerdict preview block for synthetic rows now that the same content renders inside the collapsed diagnostic panel -- it was both a duplicate and defeated the progressive-disclosure default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
….1.2) Adds backlog-item-detail-redesign.spec.ts covering: Lifecycle Summary visible with zero prior clicks, expanding a top-level Collapsible section, and revealing a synthetic headless-triage session row's TriageReviewPanel readOnly diagnostic. Follows e2e-test-conventions.md: @feature header, data-testid/ARIA locators only, no waitForTimeout. No existing fixture covered a headless-triage-* ItemSession (checked BacklogPage.ts and backlog_debug_seed_handler.go per plan.md's Unresolved Question #3), so adds a minimal handleSeedHeadlessTriageSession debug endpoint mirroring the existing handleSeedQueued/handleSeed pattern, gated to STAPLER_SQUAD_INSTANCE=e2e-local. Type-checked in isolation against the two new files (zero errors); the spec was NOT run against a live server in this environment.
…rk BacklogItemCard tested (Story 6.1.3) Adds `// +feature:` markers to LifecycleSummary.tsx and SessionDiagnosticPanel.tsx, and per-feature registry files for both under docs/registry/features/frontend/ui/, each with tested:true and testIds populated from their existing test suites. Flips docs/registry/features/frontend/ui/backlog-item-card.json's tested flag to true with Epic 5's new BacklogItemCard.test.tsx case names. make registry-generate: unmatchedBackend unchanged (59 vs 59 pre-change); unmatchedFrontend grows by 2 (37 -> 39), both new entries being the two components just registered here. This growth is a known false-positive in gap-reporter.ts's advisory domain-matching heuristic (it token-splits on "-" and fails to recognize the "domain:feature" marker convention already used throughout this codebase, e.g. the pre-existing "backlog:item-card"/"backlog:item-detail" entries suffer the same false-positive) — not a real untested-feature gap. Every per-feature JSON touched by this change has tested:true with populated testIds; verified via a stash/regen A-B comparison (pre-change: 59/37, post-change: 59/39).
…psibleGroup CollapsibleSectionProps.defaultExpanded had no caveat noting it's a no-op when the section is rendered inside a CollapsibleGroup (the group's defaultValue controls initial open state there instead). Add a JSDoc caveat matching onExpandedChange's existing one, and a dev-mode console warning when a grouped CollapsibleSection sets defaultExpanded and/or onExpandedChange so the silent no-op is caught during development.
…x e2e feature tag - server/services/backlog_debug_seed_handler.go: remove the duplicated headlessTriageSeedUUIDPrefix constant and reference the canonical headlessTriageUUIDPrefix from backlog_service_triage.go instead, so the seed handler can't silently drift from the real prefix. - web-app/src/lib/backlog/pipelineModeDisplay.test.ts: add missing test coverage for resolvePipelineModeDisplay's 4 branches (default snapshot, unrecognized slug, drifted hash, not-drifted with both empty and matching hash). - tests/e2e/backlog-item-detail-redesign.spec.ts: replace the ad hoc `backlog:item-detail` @feature tag with the actual registered kebab-case frontend feature ids (backlog-item-detail-lifecycle-summary, backlog-item-detail-diagnostic-panel) and register this spec's test names in both features' testIds arrays. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…, trim ActionsSection props Code review follow-ups from the backlog-item-detail-ux Epic 3 extraction: - Remove BacklogItemDetail.tsx's now-dead local ActionButtonLabel — every JSX call site moved into extracted sibling components. - Extract the 3x-duplicated ActionButtonLabel into a single detail/ActionButtonLabel.tsx, imported by ActionsSection, PullRequestSection, and NotesSection. - Extract the 4x-duplicated formatDate helper into lib/backlog/formatDate.ts (verified no existing datetime/timestamp util has a compatible ISO-string signature), imported by BacklogItemDetail, SessionsSection, WorkflowHistorySection, and ProgressHistorySection. - Extract the byte-identical showMoreButton vanilla-extract style, triplicated across ProgressHistorySection.css.ts, SessionsSection.css.ts, and WorkflowHistorySection.css.ts, into a shared detailShared.css.ts. - Move ActionsSection's 4 pure item-derivations (canSpawnSession, canRunAutonomously, canShipPR, acAllComplete) from the parent into local consts inside ActionsSection itself, dropping its prop count from 15 to 11 (under the 12-prop lint threshold) with no behavior change. All 9 tracked data-testids preserved verbatim. tsc --noEmit and the targeted Jest suite (BacklogItemDetail|ActionsSection|PullRequestSection| NotesSection|SessionsSection|WorkflowHistorySection|ProgressHistorySection) pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…lpers Second half of the code-review-follow-up commit (0830114 already added the shared detail/ActionButtonLabel.tsx, lib/backlog/formatDate.ts, and detail/detailShared.css.ts) — this wires the consumer files to use them instead of their local copies: - BacklogItemDetail.tsx: delete dead local ActionButtonLabel, use shared formatDate. - ActionsSection.tsx, PullRequestSection.tsx, NotesSection.tsx: import shared ActionButtonLabel instead of each defining their own copy. - SessionsSection.tsx, WorkflowHistorySection.tsx, ProgressHistorySection.tsx: import shared formatDate. - Their .css.ts files: re-export showMoreButton from detailShared.css.ts instead of redefining the byte-identical style block. - ActionsSection.tsx: compute canSpawnSession/canRunAutonomously/ canShipPR/acAllComplete locally from the item prop instead of taking them from the parent, dropping its prop count from 15 to 11. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…TriageReviewPanel GateVerdictBoxProps and TriageReviewPanelProps kept readOnly?: boolean as a flag while their write-mode callback props (onApprove/onReopen/onOverride/ onSkipGate, onApply/onSkip) stayed required. This forced the sole readOnly consumer, SessionDiagnosticPanel, to fabricate never-called noopSync/ noopAsync/noopAsyncWithArg stand-ins just to satisfy the type checker, leaving the "wire a real callback through the readOnly branch" mistake uncaught at compile time. Convert both prop types to a discriminated union on readOnly: true (no callbacks) vs readOnly?: false (callbacks required). SessionDiagnosticPanel now passes zero callback props in its readOnly branch — the compiler enforces it instead of noop props masking it. Internal handlers narrow via an isReadOnlyProps type guard and early-return when a callback isn't present, mirroring the existing optional-onReReview pattern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…ItemDetail's own correct usage 1d8b6cd added a dev-mode console.warn in CollapsibleSection for any grouped section receiving a truthy defaultExpanded, but every one of BacklogItemDetail's 8 grouped sections legitimately passes defaultExpanded={<key>Expanded} — the same state that also drives the CollapsibleGroup's own `value` via sectionExpandEntries/openSectionKeys. That made the warning fire on every normal render, crying wolf and burying genuine misuse in noise. CollapsibleGroup now threads its resolved open-key set (value, or defaultValue when uncontrolled) through context, and CollapsibleSection only warns when a truthy defaultExpanded actually diverges from what the group says that section's state is — redundant-but-consistent usage (this codebase's actual pattern) no longer warns; genuine mismatches still do. Adds a regression test rendering BacklogItemDetail with every optional grouped section mounted (status "review", VCS data present) and asserting console.warn is never called, including after a toggle.
PR #208 review flagged that no test exercised verdict="UNVERIFIABLE" — neither the conditional "Re-run Gate" button (gated on onReReview), its click handler, nor the Reopen/Override affordances for that verdict. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
…oldest items.slice(0, cap) returned the FIRST cap elements, but every caller (SessionsSection, WorkflowHistorySection, ProgressHistorySection) passes data in ascending createdAt order from the backend (ent.Asc(FieldCreatedAt) in session/ent_repository_backlog.go and session/storage_backlog.go), so the default view showed the OLDEST triage/event/note noise instead of the most recent work — the exact inverse of Epic 3.4 / Blocker C's intent for chronically-stuck items. Switch to items.slice(-cap) to take the tail while preserving ascending display order. Strengthens regression tests across useShowMore and its three consumers to assert the IDENTITY of visible items (most-recent present, oldest absent pre-expand; oldest present post-expand), not just their count, since a count-only assertion is exactly what let the head/tail bug ship silently.
…dd diff-error test BlockedNotice.tsx's doc comment claimed a blanket "confirmed security review" for all blocked_guardrail summaries, but classifySessionKind maps two distinct backend paths to that kind: review-blocked-* (built from RunPreGateSecurityCheck, actually covered by Story 4.1.1's test) and diff-error-* (built from GetGitDiffRef's wrapped command error, never audited or tested). Names both paths explicitly and adds a regression test proving GetGitDiffRef's error never embeds command stderr/diff content, so a future change to its error wrapping would be caught before reaching this now-more-discoverable UI surface.
…verage TriageReviewPanel.test.tsx's mapBacklogItem_triageStatus_* tests hardcoded triageStatus on a hand-built BacklogItem literal and then asserted against that same literal — a tautology that could never fail regardless of what mapBacklogItem actually does. Export mapBacklogItem from useBacklogService.ts and add useBacklogService.test.ts, which feeds it realistic proto-shaped BacklogItem/ItemSession/TriageResult fixtures and asserts on the derived output, covering: no triage session, running, orphan-detected failed (item advanced past "idea" without endedAt), ended-without-result failed, ended-with-empty-summary failed, completed, and most-recent-session selection when multiple triage sessions exist.
…ead of polling independently
LifecycleSummary called useStuckBacklogItems() directly, standing up its
own transport/client and 60s poll on every render. Since BacklogItemDetail
remounts via key={selectedItemId} on every backlog item click, this fired
a fresh ListStuckBacklogItems RPC unrelated to the clicked item, and would
duplicate polling if a future page ever rendered BacklogBoard and
BacklogItemDetail together.
BacklogItemDetail now calls useStuckBacklogItems() once, resolves the
.find(i => i.itemId === item.id) match itself, and passes the result down
to LifecycleSummary as a plain stuckItem prop — mirroring the single-fetch
pattern board/page.tsx -> BacklogItemCard already establishes for the
board view.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4RHh88D6ax9iUW8vdycg7
# Conflicts: # web-app/src/components/backlog/BacklogItemDetail.tsx
Contributor
UX Analysis
|
Contributor
🎬 E2E Feature Demos2 shard(s) recorded feature flows for this PR. recordings shard 1 Demo preview opens directly in browser (single-file HTML). Raw WebM recordings in ZIP. Expires after 30 days. |
The dependency was added via npm (package.json + package-lock.json) but this repo's CI uses `pnpm install --frozen-lockfile`, which fails immediately when pnpm-lock.yaml doesn't match package.json — breaking every frontend CI job (Build, Lint, UX Analysis, Registry Validation, Frontend Bundle Size).
Contributor
✅ Registry ValidationTest Coverage: 17/178 features have
|
Contributor
Go Benchmarks (Tier 1) |
Contributor
E2E RPC Latency |
Contributor
📊 Feature E2E CoverageFeature E2E coverage: 5/178 tested (3%)
|
Contributor
Frontend Terminal Throughput |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Redesigns the backlog item detail panel with progressive disclosure, an always-visible lifecycle summary, and — most importantly — fixes the bug where triage sessions and blocked/manual-review sessions rendered as inert text or dead links instead of being inspectable. When an item is stuck in review, you can now actually see why.
Context
The detail panel (
BacklogItemDetail.tsx, 1577 lines) rendered 12+ sections simultaneously with no progressive disclosure, duplicated status/PR/session info across multiple locations, and gave no way to inspect the triage or headless-review session that produced a stuck state — you had to infer the cause from Progress History text. Research during planning found the deeper issue: the diagnostic data (triage results, review verdicts) was already being fetched over the wire but silently dropped at render time, not missing from the backend.Changes
Collapsible/CollapsibleGroup(Radix-backed, keyboard-navigable accordion), a memoizedcurrentWorkSessionselector, a closedSessionKindclassifier, and a sharedBlockerChip./unfinished) + Liveness Line — replacing a status badge that was duplicated across 3+ locations.TriageReviewPanel/GateVerdictBoxin a new read-only mode) instead of inert text or a dead link — no new backend RPC needed, the data was already there.BacklogItemCardgained the same canonical status label and blocker-chip signal the detail view uses.itemIdstate-leak bug (manual-review form staying open across item switches), a double-submit hazard (poll firing mid in-flight Approve/Override), and a dead-link case (manual-review-*/diff-error-*sessions rendering as clickable links to nothing).Impact
web-app/src/components/backlog/(detail panel, board card, shared VCS widget),server/services/backlog_debug_seed_handler.go(e2e-only debug endpoint),session/backlog_review_test.go(new regression test),tests/e2e/.VcsPanel.tsxandUnfinishedItemDetail.tsx(the separate/unfinishedpage) are explicitly untouched; verified via empty diff.@radix-ui/react-accordion(same vendor family as the existing Dialog/Tabs/Tooltip usage) — see ADR-027.Reviewer Notes
SessionKindclassification (web-app/src/lib/backlog/sessionKind.ts) and its dispatch inSessionDiagnosticPanel.tsx— this is what fixes the original bug; theshowPrLinkopt-out prop onVcsWidget/VcsWidgetGithubRow(scoped narrowly to avoid touching the out-of-scope/unfinishedpage).ActionsSection.tsx(343 lines) is a relocation of the original status-conditional block, not a full decomposition — flagged during planning as an acceptable follow-up given appetite, not missed.BacklogItemDetail.css.tsremains a shared stylesheet several extracted sections still import from — follow-up debt, not a functional issue.ActionsSectioninto per-status subcomponents; migrate the remaining sections off the sharedBacklogItemDetail.css.tsgrab-bag; extend the compactBlockerChipto/backlog's non-board list view if row width ever allows (currently deferred with reasoning inBacklogItemBadge.tsx).Verification
Full SDD workflow (ideate → research → plan → validate → implement → verify) ran end-to-end, including an architecture review, adversarial review, pre-mortem, and a PM/UX/Engineering triad review during planning, and a 4-layer verify pass (idioms, architecture, correctness/tests, live UX/behavioral) after implementation. All layers passed clean; see
project_plans/backlog-item-detail-ux/for the full trail (requirements, 6 research docs, plan, ADR-027, UX design, review docs).Related
Closes the UX gaps described in the original request: progressive disclosure, at-a-glance lifecycle state, and visibility into triage/review sessions.