Skip to content

feat(backlog): progressive disclosure + session diagnostics for item detail panel - #208

Merged
tstapler merged 31 commits into
mainfrom
stapler-squad-bk-ux
Jul 22, 2026
Merged

feat(backlog): progressive disclosure + session diagnostics for item detail panel#208
tstapler merged 31 commits into
mainfrom
stapler-squad-bk-ux

Conversation

@tstapler

Copy link
Copy Markdown
Owner

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

  • Shared primitives: Collapsible/CollapsibleGroup (Radix-backed, keyboard-navigable accordion), a memoized currentWorkSession selector, a closed SessionKind classifier, and a shared BlockerChip.
  • Lifecycle Summary: an always-visible header — Stage Tracker (bound to real backend status) + Blocker Chip (reusing the existing stuck-item detection system, previously only surfaced on /unfinished) + Liveness Line — replacing a status badge that was duplicated across 3+ locations.
  • Progressive disclosure: the panel's 12+ sections extracted into collapsible siblings with status-aware default-expand state; long lists (sessions, workflow history, progress history) cap to the most recent N with a persisted "Show more."
  • Session diagnostics (the core fix): triage sessions and blocked/manual-review sessions now render their actual structured diagnostic content (reusing TriageReviewPanel/GateVerdictBox in a new read-only mode) instead of inert text or a dead link — no new backend RPC needed, the data was already there.
  • Board card consistency: BacklogItemCard gained the same canonical status label and blocker-chip signal the detail view uses.
  • Bug fixes found along the way: an itemId state-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).
  • Tests: 364 Jest tests (unit + integration), a new Playwright e2e spec run live against an isolated server, a Go regression test proving the security-guardrail error text never leaks a raw secret substring.

Impact

  • Scope: 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/.
  • Breaking Changes: none — VcsPanel.tsx and UnfinishedItemDetail.tsx (the separate /unfinished page) are explicitly untouched; verified via empty diff.
  • Performance: neutral — pure UI/display feature, no new backend calls on the hot path.
  • Dependencies: adds @radix-ui/react-accordion (same vendor family as the existing Dialog/Tabs/Tooltip usage) — see ADR-027.

Reviewer Notes

  • Focus areas: the SessionKind classification (web-app/src/lib/backlog/sessionKind.ts) and its dispatch in SessionDiagnosticPanel.tsx — this is what fixes the original bug; the showPrLink opt-out prop on VcsWidget/VcsWidgetGithubRow (scoped narrowly to avoid touching the out-of-scope /unfinished page).
  • Known limitations: 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.ts remains a shared stylesheet several extracted sections still import from — follow-up debt, not a functional issue.
  • Follow-up tasks: decompose ActionsSection into per-status subcomponents; migrate the remaining sections off the shared BacklogItemDetail.css.ts grab-bag; extend the compact BlockerChip to /backlog's non-board list view if row width ever allows (currently deferred with reasoning in BacklogItemBadge.tsx).
  • Rollback procedure: standard revert via PR close + revert commit — the redesign shipped as one PR but each epic's commits are independently identifiable if a partial revert is ever needed.
  • Feature flag: not gated — single-user internal tool, no production rollout process beyond this PR.
  • Feature flag cleanup: N/A — not gated.

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.

tstapler and others added 30 commits July 21, 2026 20:25
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
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

UX Analysis

Check Status Details
✅ Axe Core (WCAG 2.1 AA) success Critical/serious violations block merge
⚠️ Lighthouse Performance Score: unknown Warning if < 70 (non-blocking)
🤖 Claude UX Analysis Advisory See docs/qa/ for findings

Axe Core excludes terminal rendering areas (intentional design).
Lighthouse runs in desktop preset for this developer tool.

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

🎬 E2E Feature Demos

2 shard(s) recorded feature flows for this PR.

recordings shard 1
recordings shard 2

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).
@github-actions

Copy link
Copy Markdown
Contributor

✅ Registry Validation

Registry Validation
===================

Building backend scanner...
Scanning backend features...
Wrote 117 feature files to /tmp/tmp.fAafgDEPkq/backend
Wrote 15 feature files to /tmp/tmp.fAafgDEPkq/backend
Wrote 44 feature files to /tmp/tmp.fAafgDEPkq/backend
Wrote 7 feature files to /tmp/tmp.fAafgDEPkq/backend
Wrote 11 feature files to /tmp/tmp.fAafgDEPkq/backend

=== Backend Registry Diff ===
Committed: 178  Generated: 178  Divergence: 0.0%
⚠️  111 feature(s) missing // +api: marker (markerFound: false)

✅ Registry validation passed. Divergence: 0.0%

Test Coverage: 17/178 features have testIds (9.6%)

Divergence > 2% blocks merges. Coverage reporting is advisory only.

@github-actions

Copy link
Copy Markdown
Contributor

Go Benchmarks (Tier 1)

benchmarks/go/tier1-baseline.txt:98: missing iteration count
benchmarks/go/tier1-baseline.txt:197: missing iteration count
tier1-bench.txt:98: missing iteration count
tier1-bench.txt:198: missing iteration count
goos: linux
goarch: amd64
pkg: github.com/tstapler/stapler-squad/session
cpu: AMD EPYC 7763 64-Core Processor                
                                            │ benchmarks/go/tier1-baseline.txt │
                                            │              sec/op              │
CircularBufferWrite_4KB-4                                         80.18n ±  1%
CircularBufferWrite_4KB_Allocs-4                                  83.25n ±  2%
CircularBufferGetRecent_4KB-4                                     500.9n ±  4%
CircularBufferGetAll-4                                            4.073µ ± 12%
GetTimeSinceLastMeaningfulOutput_HotPath-4                        65.85n ±  0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       32.77n ±  1%
geomean                                                           175.7n

                                            │ benchmarks/go/tier1-baseline.txt │
                                            │               B/op               │
CircularBufferWrite_4KB-4                                         0.000 ± 0%
CircularBufferWrite_4KB_Allocs-4                                  0.000 ± 0%
CircularBufferGetRecent_4KB-4                                   4.000Ki ± 0%
CircularBufferGetAll-4                                          40.00Ki ± 0%
GetTimeSinceLastMeaningfulOutput_HotPath-4                        0.000 ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       0.000 ± 0%
geomean                                                                      ¹
¹ summaries must be >0 to compute geomean

                                            │ benchmarks/go/tier1-baseline.txt │
                                            │            allocs/op             │
CircularBufferWrite_4KB-4                                         0.000 ± 0%
CircularBufferWrite_4KB_Allocs-4                                  0.000 ± 0%
CircularBufferGetRecent_4KB-4                                     1.000 ± 0%
CircularBufferGetAll-4                                            1.000 ± 0%
GetTimeSinceLastMeaningfulOutput_HotPath-4                        0.000 ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4                       0.000 ± 0%
geomean                                                                      ¹
¹ summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │
                              │               B/s                │
CircularBufferWrite_4KB-4                           47.57Gi ± 2%
CircularBufferGetRecent_4KB-4                       7.616Gi ± 4%
geomean                                             19.03Gi

cpu: AMD EPYC 9V74 80-Core Processor                
                                            │ tier1-bench.txt │
                                            │     sec/op      │
CircularBufferWrite_4KB-4                         80.78n ± 1%
CircularBufferWrite_4KB_Allocs-4                  80.58n ± 1%
CircularBufferGetRecent_4KB-4                     546.4n ± 2%
CircularBufferGetAll-4                            3.766µ ± 1%
GetTimeSinceLastMeaningfulOutput_HotPath-4        69.95n ± 1%
GetTimeSinceLastMeaningfulOutput_ColdPath-4       34.61n ± 1%
geomean                                           178.6n

                                            │ tier1-bench.txt │
                                            │      B/op       │
CircularBufferWrite_4KB-4                        0.000 ± 0%
CircularBufferWrite_4KB_Allocs-4                 0.000 ± 0%
CircularBufferGetRecent_4KB-4                  4.000Ki ± 0%
CircularBufferGetAll-4                         40.00Ki ± 0%
GetTimeSinceLastMeaningfulOutput_HotPath-4       0.000 ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4      0.000 ± 0%
geomean                                                     ¹
¹ summaries must be >0 to compute geomean

                                            │ tier1-bench.txt │
                                            │    allocs/op    │
CircularBufferWrite_4KB-4                        0.000 ± 0%
CircularBufferWrite_4KB_Allocs-4                 0.000 ± 0%
CircularBufferGetRecent_4KB-4                    1.000 ± 0%
CircularBufferGetAll-4                           1.000 ± 0%
GetTimeSinceLastMeaningfulOutput_HotPath-4       0.000 ± 0%
GetTimeSinceLastMeaningfulOutput_ColdPath-4      0.000 ± 0%
geomean                                                     ¹
¹ summaries must be >0 to compute geomean

                              │ tier1-bench.txt │
                              │       B/s       │
CircularBufferWrite_4KB-4          47.22Gi ± 2%
CircularBufferGetRecent_4KB-4      6.981Gi ± 2%
geomean                            18.16Gi

pkg: github.com/tstapler/stapler-squad/session/detection/ratelimit
cpu: AMD EPYC 7763 64-Core Processor                
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
StripANSI_PlainText-4                                6.876n ± 0%
StripANSI_WithEscapes-4                              756.8n ± 1%
ProcessOutput_InactiveState-4                        6.289n ± 1%
geomean                                              31.99n

                              │ benchmarks/go/tier1-baseline.txt │
                              │               B/op               │
StripANSI_PlainText-4                               0.000 ± 0%
StripANSI_WithEscapes-4                             136.0 ± 0%
ProcessOutput_InactiveState-4                       0.000 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │
                              │            allocs/op             │
StripANSI_PlainText-4                               0.000 ± 0%
StripANSI_WithEscapes-4                             5.000 ± 0%
ProcessOutput_InactiveState-4                       0.000 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                              │ tier1-bench.txt │
                              │     sec/op      │
StripANSI_PlainText-4               7.046n ± 5%
StripANSI_WithEscapes-4             654.4n ± 1%
ProcessOutput_InactiveState-4       6.630n ± 0%
geomean                             31.27n

                              │ tier1-bench.txt │
                              │      B/op       │
StripANSI_PlainText-4              0.000 ± 0%
StripANSI_WithEscapes-4            136.0 ± 0%
ProcessOutput_InactiveState-4      0.000 ± 0%
geomean                                       ¹
¹ summaries must be >0 to compute geomean

                              │ tier1-bench.txt │
                              │    allocs/op    │
StripANSI_PlainText-4              0.000 ± 0%
StripANSI_WithEscapes-4            5.000 ± 0%
ProcessOutput_InactiveState-4      0.000 ± 0%
geomean                                       ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/queue
cpu: AMD EPYC 7763 64-Core Processor                
                              │ benchmarks/go/tier1-baseline.txt │
                              │              sec/op              │
ReviewQueue_ConcurrentReads-4                        92.97n ± 1%
ReviewQueue_Add-4                                    514.6n ± 1%
geomean                                              218.7n

                              │ benchmarks/go/tier1-baseline.txt │
                              │               B/op               │
ReviewQueue_ConcurrentReads-4                       0.000 ± 0%
ReviewQueue_Add-4                                   640.0 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

                              │ benchmarks/go/tier1-baseline.txt │
                              │            allocs/op             │
ReviewQueue_ConcurrentReads-4                       0.000 ± 0%
ReviewQueue_Add-4                                   4.000 ± 0%
geomean                                                        ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                              │ tier1-bench.txt │
                              │     sec/op      │
ReviewQueue_ConcurrentReads-4      82.14n ± 11%
ReviewQueue_Add-4                  496.9n ±  1%
geomean                            202.0n

                              │ tier1-bench.txt │
                              │      B/op       │
ReviewQueue_ConcurrentReads-4      0.000 ± 0%
ReviewQueue_Add-4                  640.0 ± 0%
geomean                                       ¹
¹ summaries must be >0 to compute geomean

                              │ tier1-bench.txt │
                              │    allocs/op    │
ReviewQueue_ConcurrentReads-4      0.000 ± 0%
ReviewQueue_Add-4                  4.000 ± 0%
geomean                                       ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/scrollback
cpu: AMD EPYC 7763 64-Core Processor                
                                      │ benchmarks/go/tier1-baseline.txt │
                                      │              sec/op              │
CircularBuffer_ConcurrentReadWrite-4                         3.860µ ± 1%
CircularBuffer_BurstAppend-4                                 101.8µ ± 1%
CircularBuffer_GetLastN_LargeBuffer-4                        20.30µ ± 9%
CircularBuffer_GetRange_Sequential-4                         15.31µ ± 6%
CircularBufferAppend-4                                       99.91n ± 1%
CircularBufferGetLastN-4                                     2.631µ ± 2%
CircularBufferConcurrentAppend-4                             130.7n ± 1%
geomean                                                      3.292µ

                                      │ benchmarks/go/tier1-baseline.txt │
                                      │               B/op               │
CircularBuffer_ConcurrentReadWrite-4                        6.062Ki ± 0%
CircularBuffer_BurstAppend-4                                62.50Ki ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                       56.00Ki ± 0%
CircularBuffer_GetRange_Sequential-4                        28.00Ki ± 0%
CircularBufferAppend-4                                        24.00 ± 0%
CircularBufferGetLastN-4                                    6.000Ki ± 0%
CircularBufferConcurrentAppend-4                              32.00 ± 0%
geomean                                                     3.077Ki

                                      │ benchmarks/go/tier1-baseline.txt │
                                      │            allocs/op             │
CircularBuffer_ConcurrentReadWrite-4                          2.000 ± 0%
CircularBuffer_BurstAppend-4                                 1.000k ± 0%
CircularBuffer_GetLastN_LargeBuffer-4                         1.000 ± 0%
CircularBuffer_GetRange_Sequential-4                          1.000 ± 0%
CircularBufferAppend-4                                        1.000 ± 0%
CircularBufferGetLastN-4                                      1.000 ± 0%
CircularBufferConcurrentAppend-4                              1.000 ± 0%
geomean                                                       2.962

                             │ benchmarks/go/tier1-baseline.txt │
                             │               B/s                │
CircularBuffer_BurstAppend-4                       599.5Mi ± 1%

cpu: AMD EPYC 9V74 80-Core Processor                
                                      │ tier1-bench.txt │
                                      │     sec/op      │
CircularBuffer_ConcurrentReadWrite-4        3.441µ ± 2%
CircularBuffer_BurstAppend-4                106.3µ ± 0%
CircularBuffer_GetLastN_LargeBuffer-4       20.18µ ± 1%
CircularBuffer_GetRange_Sequential-4        13.21µ ± 4%
CircularBufferAppend-4                      104.3n ± 0%
CircularBufferGetLastN-4                    2.593µ ± 1%
CircularBufferConcurrentAppend-4            138.0n ± 0%
geomean                                     3.226µ

                                      │ tier1-bench.txt │
                                      │      B/op       │
CircularBuffer_ConcurrentReadWrite-4       6.062Ki ± 0%
CircularBuffer_BurstAppend-4               62.50Ki ± 0%
CircularBuffer_GetLastN_LargeBuffer-4      56.00Ki ± 0%
CircularBuffer_GetRange_Sequential-4       28.00Ki ± 0%
CircularBufferAppend-4                       24.00 ± 0%
CircularBufferGetLastN-4                   6.000Ki ± 0%
CircularBufferConcurrentAppend-4             32.00 ± 0%
geomean                                    3.077Ki

                                      │ tier1-bench.txt │
                                      │    allocs/op    │
CircularBuffer_ConcurrentReadWrite-4         2.000 ± 0%
CircularBuffer_BurstAppend-4                1.000k ± 0%
CircularBuffer_GetLastN_LargeBuffer-4        1.000 ± 0%
CircularBuffer_GetRange_Sequential-4         1.000 ± 0%
CircularBufferAppend-4                       1.000 ± 0%
CircularBufferGetLastN-4                     1.000 ± 0%
CircularBufferConcurrentAppend-4             1.000 ± 0%
geomean                                      2.962

                             │ tier1-bench.txt │
                             │       B/s       │
CircularBuffer_BurstAppend-4      574.4Mi ± 1%

pkg: github.com/tstapler/stapler-squad/session/tmux
cpu: AMD EPYC 7763 64-Core Processor                
                             │ benchmarks/go/tier1-baseline.txt │
                             │              sec/op              │
StripANSICodes_PlainText-4                          6.880n ± 0%
StripANSICodes_WithEscapes-4                        697.1n ± 0%
IsBanner_PlainText-4                                478.6n ± 1%
geomean                                             131.9n

                             │ benchmarks/go/tier1-baseline.txt │
                             │               B/op               │
StripANSICodes_PlainText-4                         0.000 ± 0%
StripANSICodes_WithEscapes-4                       56.00 ± 0%
IsBanner_PlainText-4                               0.000 ± 0%
geomean                                                       ¹
¹ summaries must be >0 to compute geomean

                             │ benchmarks/go/tier1-baseline.txt │
                             │            allocs/op             │
StripANSICodes_PlainText-4                         0.000 ± 0%
StripANSICodes_WithEscapes-4                       4.000 ± 0%
IsBanner_PlainText-4                               0.000 ± 0%
geomean                                                       ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                             │ tier1-bench.txt │
                             │     sec/op      │
StripANSICodes_PlainText-4        7.080n ± 10%
StripANSICodes_WithEscapes-4      618.4n ±  0%
IsBanner_PlainText-4              472.4n ±  1%
geomean                           127.4n

                             │ tier1-bench.txt │
                             │      B/op       │
StripANSICodes_PlainText-4        0.000 ± 0%
StripANSICodes_WithEscapes-4      56.00 ± 0%
IsBanner_PlainText-4              0.000 ± 0%
geomean                                      ¹
¹ summaries must be >0 to compute geomean

                             │ tier1-bench.txt │
                             │    allocs/op    │
StripANSICodes_PlainText-4        0.000 ± 0%
StripANSICodes_WithEscapes-4      4.000 ± 0%
IsBanner_PlainText-4              0.000 ± 0%
geomean                                      ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/tokens
cpu: AMD EPYC 7763 64-Core Processor                
                                   │ benchmarks/go/tier1-baseline.txt │
                                   │              sec/op              │
TokenParser_ProcessUserEntry-4                            5.446m ± 2%
DetectCommandsInText/NoSlash-4                            7.503n ± 0%
DetectCommandsInText/WithCommand-4                        1.673µ ± 0%
geomean                                                   4.089µ

                                   │ benchmarks/go/tier1-baseline.txt │
                                   │               B/op               │
TokenParser_ProcessUserEntry-4                         11.02Mi ± 0%
DetectCommandsInText/NoSlash-4                           0.000 ± 0%
DetectCommandsInText/WithCommand-4                       433.0 ± 0%
geomean                                                             ¹
¹ summaries must be >0 to compute geomean

                                   │ benchmarks/go/tier1-baseline.txt │
                                   │            allocs/op             │
TokenParser_ProcessUserEntry-4                           34.00 ± 0%
DetectCommandsInText/NoSlash-4                           0.000 ± 0%
DetectCommandsInText/WithCommand-4                       6.000 ± 0%
geomean                                                             ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                                   │ tier1-bench.txt │
                                   │     sec/op      │
TokenParser_ProcessUserEntry-4           5.535m ± 1%
DetectCommandsInText/NoSlash-4           6.344n ± 1%
DetectCommandsInText/WithCommand-4       1.504µ ± 1%
geomean                                  3.751µ

                                   │ tier1-bench.txt │
                                   │      B/op       │
TokenParser_ProcessUserEntry-4        11.02Mi ± 0%
DetectCommandsInText/NoSlash-4          0.000 ± 0%
DetectCommandsInText/WithCommand-4      433.0 ± 0%
geomean                                            ¹
¹ summaries must be >0 to compute geomean

                                   │ tier1-bench.txt │
                                   │    allocs/op    │
TokenParser_ProcessUserEntry-4          34.00 ± 0%
DetectCommandsInText/NoSlash-4          0.000 ± 0%
DetectCommandsInText/WithCommand-4      6.000 ± 0%
geomean                                            ¹
¹ summaries must be >0 to compute geomean

pkg: github.com/tstapler/stapler-squad/session/unfinished
cpu: AMD EPYC 7763 64-Core Processor                
                               │ benchmarks/go/tier1-baseline.txt │
                               │              sec/op              │
DiffShortstat/GitVCSReader-4                          3.302m ± 2%
DiffShortstat/GoGitVCSReader-4                        76.89n ± 0%
DiffShortstatCached-4                                 75.32n ± 1%
geomean                                               2.674µ

                               │ benchmarks/go/tier1-baseline.txt │
                               │               B/op               │
DiffShortstat/GitVCSReader-4                       62.57Ki ± 0%
DiffShortstat/GoGitVCSReader-4                       0.000 ± 0%
DiffShortstatCached-4                                0.000 ± 0%
geomean                                                         ¹
¹ summaries must be >0 to compute geomean

                               │ benchmarks/go/tier1-baseline.txt │
                               │            allocs/op             │
DiffShortstat/GitVCSReader-4                         360.0 ± 0%
DiffShortstat/GoGitVCSReader-4                       0.000 ± 0%
DiffShortstatCached-4                                0.000 ± 0%
geomean                                                         ¹
¹ summaries must be >0 to compute geomean

cpu: AMD EPYC 9V74 80-Core Processor                
                               │ tier1-bench.txt │
                               │     sec/op      │
DiffShortstat/GitVCSReader-4         3.354m ± 1%
DiffShortstat/GoGitVCSReader-4       81.53n ± 1%
DiffShortstatCached-4                80.77n ± 1%
geomean                              2.806µ

                               │ tier1-bench.txt │
                               │      B/op       │
DiffShortstat/GitVCSReader-4      56.57Ki ± 0%
DiffShortstat/GoGitVCSReader-4      0.000 ± 0%
DiffShortstatCached-4               0.000 ± 0%
geomean                                        ¹
¹ summaries must be >0 to compute geomean

                               │ tier1-bench.txt │
                               │    allocs/op    │
DiffShortstat/GitVCSReader-4        360.0 ± 0%
DiffShortstat/GoGitVCSReader-4      0.000 ± 0%
DiffShortstatCached-4               0.000 ± 0%
geomean                                        ¹
¹ summaries must be >0 to compute geomean

@github-actions

Copy link
Copy Markdown
Contributor

E2E RPC Latency

list-sessions-ttfb-mean: 6ms (▼ faster -12.1%; baseline: 7ms)
list-sessions-total-mean: 12ms (▲ slower +48.2%; baseline: 8ms)

@github-actions

Copy link
Copy Markdown
Contributor

📊 Feature E2E Coverage

Feature E2E coverage: 5/178 tested (3%)

Run make e2e-report locally to view the full Allure report.

@github-actions

Copy link
Copy Markdown
Contributor

Frontend Terminal Throughput

terminal-throughput-mean: 14 KB/s ▼ -0.3% (baseline: 14 KB/s)
terminal-throughput-p50: 16 KB/s ▲ +0.8% (baseline: 16 KB/s)

@tstapler
tstapler merged commit b7f0459 into main Jul 22, 2026
24 checks passed
@tstapler
tstapler deleted the stapler-squad-bk-ux branch July 22, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant