Skip to content

feat(sessions): redesign the session list rows - #3766

Merged
michellemxm merged 1 commit into
mainfrom
feat/session-list-ui
Aug 15, 2026
Merged

feat(sessions): redesign the session list rows#3766
michellemxm merged 1 commit into
mainfrom
feat/session-list-ui

Conversation

@michellemxm

@michellemxm michellemxm commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Problem

The middle scrollable session list in the sessions panel had drifted from the current design (Figma node 3209:1006). Status was scattered — a running dot before the secondary line, an unread dot pinned to the row's far right, six other status glyphs each rendered inline before their own subtitle — so a row had no single place to look for "what is this session doing". Session context was absent from the row's meta line, tags rendered as bordered chips below the title, and the left colour bar floated 5px inside the row rather than marking its edge.

Why it matters

The session list is the primary navigation surface of the dashboard; a designer scanning 40+ rows needs one predictable spot per signal. Scattered status markers and duplicated context (folder chip + tag chip both spelling the project name) add visual noise that slows the scan, and a colour bar that doesn't touch the row edge reads as decoration rather than identity.

Fix (symptoms → root cause → change)

The row had accreted markers in whatever spot each feature landed in. The redesign gives each concern one home:

  • Meta line now reads agent · tag · tag … timestamp [pin]. The session's tags render as tinted plain text (each in its own tag colour, no border) after the agent name; the pin moved to the right of the timestamp. Tags are the context the row needs — the auto-tagger already labels each session with its project — so no separate repo-name derivation is added.
  • Status gutter: all 8 status indicators (pending-approval, sub-agent awaiting approval, unanswered question, goal loop, workflow running, sub-agents running, running, unread) consolidated into one reserved-width gutter left of the headline, so every row's meta/headline/secondary/chip content shares one left edge. Running renders a Loader2 spinner (was a pulsing dot); the unread "your turn" dot moved into the gutter and now sits last in precedence (it yields to any more specific state). Distinct glyphs replace the former bare dots: Goal (loop), ShieldCheck (needs approval), MessageCircleQuestionMark (needs your answer).
  • Colour bar sits flush against the row's left edge (left:0) with length = row height − 2 × corner radius, expressed as a var(--radius-md) vertical inset so it tracks the theme's radius instead of a hardcoded pixel. Applied identically to the solid and gradient colour modes.
  • Spacing: session rows tightened to px-3 / gap-1.5; the row divider is inset to the content x (ml-[30px]) so it underlines the text block rather than boxing the whole row; folder rows use symmetric px-2.5 (the inline 16px left-pad override removed); the nested connector line indents ml-2.
  • The separate bordered tag-chip row is removed — every tag now lives in the meta line, printed exactly once.

Tests

  • ChatSidebar.metaLine.test.tsx (new) — pins all-tags-inline rendering, tag order, tinted-no-border, the dangling-dot guard, pin-last ordering, and no-separate-chip-row.
  • ChatSidebar.statusGutter.test.tsx (new) — pins one glyph per row, the subtitle-chain precedence (owed decision > working signal), the spinner-not-dot for running, the unread dot in the gutter, reserved width when idle, the accessible name, and each distinct glyph (ShieldCheck / MessageCircleQuestionMark / Goal).
  • sessionColorBar.test.ts (new) — a rule-text drift guard over index.css: flush-left, radius-derived inset, pill radius, both colour-mode selectors agree.
  • ChatSidebar.folderAlignment.test.tsx — updated to the new folder-header geometry (px-2.5 pad, no inline override) with token-exact class assertions (substring matching would pass vacuously on a fractional Tailwind step).
  • ChatSidebar.flatView.test.tsx, ChatSidebar.listTags.test.tsx — updated: the flat-view folder annotation and the list-view tag pills are gone; tags render in the meta line.

Manual verification

Verified each increment against a loopback preview that serves the real built bundle with fixtures (meta line, status gutter, colour bar, spacing, folder rows) and read every frame. Screenshots below are from that preview. All local gates green on the final commit: tsc -b, eslint (0 errors), I18N_BASE_REF=origin/main npm run i18n:check, brand gate, and 676 sidebar/chat/colour-bar tests.

Screenshots

Redesigned session list: agent · tag meta line, status gutter with distinct glyphs, flush colour bars

Tag run ellipsizes at a narrow sidebar width

At 210px the multi-tag run truncates with an ellipsis, not mid-word

Deliberately traded / not included

  • Folder-header alignment guides (the #1211 regression guards) no longer hold: the status gutter shifts a session row's content 12px right, so the folder glyph/name no longer land on sibling/child session text. This was a measured guide; it is abandoned on purpose and ChatSidebar.folderAlignment.test.tsx now pins the folder row's own proportions instead. The folder row reads as a header over its sessions rather than a peer.
  • Flat-view location context for untagged sessions: removing the folder chip means an untagged session in flat view shows no on-row location. Open question for the maintainer (see review replies) — whether to show the folder name as a fallback when a session has no tags.

no linked issue: iterative design-driven redesign, no tracked issue.

@michellemxm
michellemxm requested a review from a team August 15, 2026 06:42
@michellemxm
michellemxm requested a review from a team as a code owner August 15, 2026 06:42
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 15, 2026
@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — 🟡 CONCERNS

Advisory design-level review of 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc — updated in place on each push; does not block merge.

Design-Verdict: CONCERNS

Sound consolidation, but row context now hangs entirely on the auto-tagger, and the unread signal's new ranking contradicts the PR's own precedence principle.

Watch

  • Removing the folder chip makes tags the only on-row location cue: an untagged session in flat view (auto-tagger off, failed, or pre-existing sessions) shows nothing, on what the description calls "the primary navigation surface". The PR itself leaves this as an "open question for the maintainer" — decide it before merge, not after, since the fallback also means re-adding the in_folder key just deleted from 13 locale files.
  • The gutter's own comment states "an owed decision outranks every 'working' signal, because a decision rendered as work in progress is how an owed approval goes unnoticed" — yet unread ("agent finished, your turn"), which is also an owed action, now "sits LAST" and is masked by a workflow or sub-agent glyph. A parent turn awaiting the user behind a long-running workflow shows only a spinner, reproducing exactly the failure mode the comment warns about. Worth an explicit call on whether unread should outrank the passive working states.

Suggestions

  • If the folder-name fallback lands, render it in the meta line's tag slot only when tags.length === 0, rather than resurrecting the trailing chip — keeps the one-home-per-concern design intact.

[DESIGN-REVIEWED] 9c2403e

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 9c2403e

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

Advisory UX-level review of 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc — updated in place on each push; does not block merge.

UX-Verdict: CONCERNS

The redesign's flagship signal — tags as row context — ellipsizes to nothing at the demonstrated width, and the status gutter's own tooltip can never appear.

Watch

  • Tags are illegible at the shipped width. The timestamp and pin are shrink-0 while the tag run is the only truncating node, so in 01-session-list.png most rows read default · …, · Pla…, · Kir… — the context the PR exists to add carries near-zero information on every scan (high frequency, every row, persistent). Smallest fix: compact the timestamp (time-only when "Yesterday") or guarantee the tag run a minimum width before ellipsis.
  • The gutter tooltip is dead. The gutter div carries title={status.label} but also pointer-events-none, which blocks hover — the title never shows. Every glyph except unread has a secondary-line text label, so the unread dot ("agent finished — your turn") becomes the one state with no discoverable meaning for mouse users; the old right-edge dot's title worked. Fix: drop pointer-events-none or hoist the title to a hoverable wrapper.
  • Untagged sessions in flat view lose all location context (folder chip removed, in_folder key deleted); the only recovery is the context menu. The author's own open question — resolve it before merge with the proposed folder-name fallback when resolvedSlotTags is empty.
  • Arbitrary per-tag colors now render as bare 11px text (style={{ color: t.color }}); the removed chip's tinted background buffered contrast. A light tag color on the light theme may be unreadable — only dark screenshots were provided.

Suggestions

  • Swap ShieldCheck for ShieldAlert/ShieldQuestion on the needs-approval gutter state — a check inside a shield reads "verified/approved", the opposite of awaiting approval.

[UX-REVIEWED] 9c2403e

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc — this comment is updated in place on each push.

Review details

All variables are defined before use, the removed in_folder key is gone consistently across all locales with no remaining references, and the status-object precedence matches the subtitle chain. This is a presentation-only change with no reachable defect. The candidate list correctly found nothing, and my independent pass confirms it.

No findings.

[OPUS-REVIEWED] 9c2403e

Verdict parsed from the review's SHA-scoped output markers for commit 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Advisory premise-level review of 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push; does not block merge.

Evidence gathered: I read the contract, the intent file, the full patch, the surrounding ChatSidebar.tsx (both the new gutter chain and the untouched subtitle chain), the CSS token definitions, and grepped for existing mechanisms (in_folder consumers, --radius-md definers, existing CSS rule-text tests, inferLane). Final review follows.

First-Principles-Verdict: CONCERNS

The gutter re-spells the subtitle chain's seven-branch precedence a second time — the exact drift the comment claims it prevents.

What this change ships

Intent: give every session-row signal one predictable home so a user scanning many rows finds status, context, and identity in fixed spots — an ADDITION (design-driven redesign), and the diff matches the stated job.

  1. All tags render as tinted text in the meta line after the agent name — justified
  2. The separate bordered tag-chip row is deleted — justified (deletion, prints each tag once)
  3. Eight scattered status markers consolidate into one gutter glyph per row — justified
  4. Running shows a spinner instead of a pulsing dot — justified relabel, declared
  5. Unread dot moved into the gutter and now yields to workflow/sub-agent states — declared changed default
  6. Approval/question/loop get distinct glyphs instead of bare dots — justified
  7. Pin moved after the timestamp — declared move, mechanical harm named (pin pushed the agent-name column)
  8. Colour bar flush-left, ends tracking the theme radius token — justified (2 definers of --radius-md counted, both themed)
  9. Flat view no longer names a session's folder; in_folder deleted from 12 locales — declared removal, trade-off left open
  10. Row/folder/divider spacing tightened; the feat: redesign sidebar folders and de-clutter the sessions sidebar #1211 alignment guides abandoned on purpose — declared

Watch

  • The status chain (ChatSidebar.tsx:2515) duplicates the subtitle chain's predicates (ChatSidebar.tsx:2796–2862): I counted 7 branch conditions and 6 label expressions spelled twice, in an order only a comment and tests hold in sync. The comment claims "glyph and secondary line can never disagree" — they can, by editing one chain. The subtraction: compute glyph and subtitle from the one resolved status, deleting the second predicate chain.
  • Item 9 ships the removal while the fallback for untagged flat-view sessions is "open question for the maintainer" — the one user-felt regression rides on a decision not yet made; keeping the chip only in flat view for untagged sessions was the smaller cut.

Subtractions

  • Fold the seven subtitle-branch predicates into the chain that already builds status (ChatSidebar.tsx:2515) — one resolved value, two renderings; delete the parallel chain at ChatSidebar.tsx:2796–2862.

[FIRST-PRINCIPLES-REVIEWED] 9c2403e

@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Aug 15, 2026
@michellemxm
michellemxm force-pushed the feat/session-list-ui branch from e66e240 to 48ca11c Compare August 15, 2026 07:04
@github-actions github-actions Bot added readiness: checking Automated validation is still running merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: action required A blocking check or review needs attention labels Aug 15, 2026
@michellemxm
michellemxm force-pushed the feat/session-list-ui branch from 48ca11c to 35ad90b Compare August 15, 2026 07:20
@michellemxm

Copy link
Copy Markdown
Contributor Author

Dispositions for the advisory reviews (Design 🟡 / UX 🟡 / First Principles 🟡) as of 35ad90bacc6e0b77b863ac08f2e8f185e1869a47:

Fixed

  • Tag run clipped mid-word with no ellipsis (UX): the meta-line tag group was inline-flex, across whose children text-overflow: ellipsis does not render — hence "KiroC" / "kc-them". Changed to a plain inline block (truncate) with inline <span> children, so a multi-tag run ellipsizes as one line while each tag keeps its own colour. Verified at a 210px sidebar: it now reads agent · … rather than a clipped fragment.
  • Stale colour-bar screenshot (UX): the previous 02-colour-bar.png predated this redesign (it showed the removed folder chip and a hover popup occluding text). Recaptured at HEAD; replaced with 02-tags-truncate.png (the narrow-width ellipsis proof) and refreshed 01-session-list.png.
  • Dead capture harness + abandoned-design comments (First Principles): deleted website/scripts/capture-session-meta-line.mjs (zero referrers, probed an agent · repo-name design that never shipped); removed the "names the session's REPO" phrasing from the derivation comment and the "identity tag wins the meta slot / status tag stays a chip / prefer-non-status" notes from ChatSidebar.metaLine.test.tsx and the "repo name carries that slot" title in ChatSidebar.flatView.test.tsx. Recorded rationale now matches shipped behaviour (all tags render inline; no chip row).

Accepted / documented

  • Undeclared folder-alignment-guide trade (First Principles): added a "Deliberately traded / not included" section to the PR body — the #1211 guides are abandoned on purpose because the status gutter shifts content 12px, and the test now pins the folder row's own proportions.

Needs a decision (maintainer) — not silently changed

  • Flat-view loses location context for untagged sessions (Design + UX + First Principles, independently): removing the folder chip means an untagged session in flat view shows only agent + timestamp. The chip removal was an explicit product request, and the suggested fallback — show the folder name in the meta slot only when a session has no tags, in flat view — is a product call, not a code fix. Flagged to the maintainer; leaving the code as requested until they rule. (In practice most sessions carry the project auto-tag, so the gap is narrow, but it is real for genuinely untagged sessions.)

Rebutted / declined (advisory suggestions)

  • Divider ml-[30px] hand-summed constant (Design suggestion): acknowledged — it is px-3 + w-3 + gap-1.5. Left inline for now: it is one call site, no test drift-guards it today, and extracting a shared token for a single consumer trades one clarity for another. Reasonable future hardening, out of scope here.
  • Clamp tag text colour to a minimum contrast (UX suggestion): the tinted-plain-text treatment (no chip background) was the explicit design direction; a contrast clamp changes the requested visual and is a separate enhancement. Deferred to the maintainer's call alongside the tinting decision.

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge and removed readiness: checking Automated validation is still running merge conflict Branch has merge conflicts with its base — author must resolve before merge labels Aug 15, 2026
@michellemxm
michellemxm enabled auto-merge (squash) August 15, 2026 19:32
@michellemxm
michellemxm force-pushed the feat/session-list-ui branch from 35ad90b to 5f72b91 Compare August 15, 2026 19:47
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: passed Eligible automated validation passed for the current revision and removed readiness: passed Eligible automated validation passed for the current revision merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Aug 15, 2026
Rework the middle scrollable session list (Figma node 3209:1006):
meta line shows agent + tags (tinted, no border) then timestamp + pin;
all 8 status indicators consolidated into one reserved-width gutter left
of the headline (spinner for running, Goal/ShieldCheck/MessageCircleQuestionMark
glyphs); session colour bar flush-left with radius-derived length; tighter
row padding (px-3/gap-1.5); inset row divider; folder rows px-2.5 with the
inline left-pad override removed.
@michellemxm
michellemxm force-pushed the feat/session-list-ui branch from 5f72b91 to 9c2403e Compare August 15, 2026 20:07
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 15, 2026
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 15, 2026
@michellemxm

Copy link
Copy Markdown
Contributor Author

Dispositions for the advisory reviews as of 9c2403ee4ba8fd1e82c8983ca01f3c94234f82bc (rebased onto main; absorbed the stalled-goal-loop feature into the status gutter):

Fixed

  • Agent name capped at max-w-[50%] even on untagged rows (First Principles, undeclared regression): the cap exists to stop a long agent name crushing the tag run — but an untagged row has no tags to protect, so the cap only truncated the agent name for no reason. Now applied only when resolvedSlotTags.length > 0; untagged rows use the full line.
  • Stalled goal loop × gutter (rebase integration): main's goalLoopStalled feature landed while this branch was open. Integrated it into the redesign — the gutter Goal glyph is static/warn when stalled and pulsing/accent when live (pulse still means "work is happening"), and the subtitle keeps main's "— interrupted" label. Updated ChatSidebar.goalLoop.test.tsx to assert the pulse on the gutter glyph (its new home) rather than the removed inline dot.

Accepted / deferred (out of scope for this PR)

  • Two precedence chains maintained twice (Design + First Principles): the gutter status ternary and the subtitle chain share the same 7-branch order, derived independently. A single ordered state-resolver feeding both would remove the drift risk. It's a real maintainability improvement but a structural refactor touching both chains — deferring to a follow-up rather than widening this PR late; filing an issue.

Needs a decision (maintainer) — explicitly your calls, not silently changed

  • Tag run legibility at default width (UX): even with the ellipsis fix (no more mid-word clip), at the default sidebar width the agent name + timestamp leave the tag run little room, so it can collapse toward ·…. The all-tags-inline-tinted treatment was your explicit design direction; the reviewer's alternative (first tag shrink-0 + a +N overflow, render nothing when nothing fits) is a different design. Your call whether to keep all-inline or move to first-tag-+N.
  • ShieldCheck vs ShieldAlert for needs-approval (UX): a checked shield can read as "already approved". You picked ShieldCheck explicitly; leaving it. Say the word and I'll swap to ShieldAlert.
  • unread demoted below workflow/sub-agent activity (Design): a background workflow can run for hours after the turn ends, hiding the "your turn" dot while you're away. This was a deliberate precedence choice (pinned in tests). Reviewer suggests ranking unread above the working signals but below owed decisions. Your call.
  • Flat-view location context for untagged sessions (all three, re-raised): still the open question from the prior round — show the folder name as a fallback only when a session has no tags, in flat view. Left as-is pending your ruling.

None of these block: the PR is green and mergeable; every item above is advisory.

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 15, 2026
@michellemxm
michellemxm merged commit d926ca5 into main Aug 15, 2026
65 of 67 checks passed
@michellemxm
michellemxm deleted the feat/session-list-ui branch August 15, 2026 22:22
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 15, 2026
CrysisDeu added a commit that referenced this pull request Aug 16, 2026
#3766 added the session-row status gutter as an in-flow flex child, which
added its 12px width plus a 6px gap to where the row's content column
starts. Measured on a real render, that pushed a child session's agent
label, title and tool-call subtitle 16px right of the folder NAME above
them, so a folder and the sessions filed under it no longer shared a left
edge and the sidebar read as two ragged columns.

Three guides now hold, at the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad, so
the folder header goes px-2.5 -> px-3.5 and the nested body ml-2 -> ml-3;
the row's own pad stays pl-3.5. The status gutter moves out of the content
flow (absolutely positioned inside that pad) — in flow it is what broke
guide 2, and no class-level assertion could see it.

Guide 3 is why the pad is 14 rather than 10: at 10 a subfolder's glyph and
its own connector line sat 4px left of the column its sibling sessions'
text starts on. Because the resulting algebra has no per-depth term, it is
scale-free — depth 3 nests exactly as depth 2 does, and root-lane sessions
land on the root folder's glyph, which restores the original pre-#1211
guide as a side effect.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

Also drop the title's mt-0.5 (row height 74 -> 62px), centre the gutter
glyph on the row instead of deriving the headline's y, use lucide Loader
for the running spinner (at 12px the LoaderCircle arc reads as a broken
ring where the spokes read as motion), and pull the empty-folder "New chat
in X" affordance onto the same column as the sessions it stands in for.

The two invariant tests #3766 rewrote to accept the misalignment are
restored, extended with guide 3 at depth 2, and now pin what actually
moved: the gutter's out-of-flow-ness. The captured numbers and the
reference frame each one is in are recorded in the test header, because
every previous rewrite of that file quoted an indent step without saying
which frame it was measured in.
CrysisDeu added a commit that referenced this pull request Aug 16, 2026
added its 12px width plus a 6px gap to where the row's content column
starts. Measured on a real render, that pushed a child session's agent
label, title and tool-call subtitle 16px right of the folder NAME above
them, so a folder and the sessions filed under it no longer shared a left
edge and the sidebar read as two ragged columns.

Three guides now hold, at the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad, so
the folder header goes px-2.5 -> px-3.5 and the nested body ml-2 -> ml-3;
the row's own pad stays pl-3.5. The status gutter moves out of the content
flow (absolutely positioned inside that pad) — in flow it is what broke
guide 2, and no class-level assertion could see it.

Guide 3 is why the pad is 14 rather than 10: at 10 a subfolder's glyph and
its own connector line sat 4px left of the column its sibling sessions'
text starts on. Because the resulting algebra has no per-depth term, it is
scale-free — depth 3 nests exactly as depth 2 does, and root-lane sessions
land on the root folder's glyph, which restores the original pre-#1211
guide as a side effect.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

Also drop the title's mt-0.5 (row height 74 -> 62px), centre the gutter
glyph on the row instead of deriving the headline's y, use lucide Loader
for the running spinner (at 12px the LoaderCircle arc reads as a broken
ring where the spokes read as motion), and pull the empty-folder "New chat
in X" affordance onto the same column as the sessions it stands in for.

The two invariant tests #3766 rewrote to accept the misalignment are
restored, extended with guide 3 at depth 2, and now pin what actually
moved: the gutter's out-of-flow-ness. The captured numbers and the
reference frame each one is in are recorded in the test header, because
every previous rewrite of that file quoted an indent step without saying
which frame it was measured in.
CrysisDeu added a commit that referenced this pull request Aug 16, 2026
A folder in the session list and the sessions filed under it did not share a
left edge. #3766 added the row's status gutter as an in-flow flex child, so
its 12px width plus a gap landed on the content column: the agent label,
title and tool-call subtitle of every session inside a folder sat well right
of that folder's own name. #3903 then tightened the same rows and got
closer, but measured on a real render it still left the folder name 1px past
its sessions' text and a nested folder's glyph 2px short of their column.

Three guides now hold, in the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad. With
D = 2, a MEASURED container differential (the header's box starts 2px left
of the nested body's box, and no class in the component expresses it):

  guide 1  P = D + M                       14 = 2 + 12
  guide 2  P + G + g = D + M + B + p + R   33 = 33
  guide 3  P = R                           14 = 14

The status gutter moves out of the content flow (absolutely positioned
inside that pad) and is centred on the row rather than deriving the
headline's y; in flow it is what broke guide 2, and no class-level assertion
could see it. Because the algebra has no per-depth term it is scale-free:
depth 3 nests as depth 2 does, and root-lane sessions land on the root
folder's glyph, which restores the original pre-#1211 guide.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

This reverts #3903's row and folder geometry, agreed with its author. Its
collapsed-folder unread dot goes back inline on the right of the name rather
than into an absolute left gutter: that gutter is what forced the header pad
to 18px, and the pad is not free — it has to equal the session row's. The
2px colour bar from #3903 is kept, being orthogonal to the guides.

Also drop the title's mt-0.5 (row height 49.6 -> 43.6px idle, 68.1 -> 62.1
running), use lucide Loader for the running spinner (at 12px the
LoaderCircle arc reads as a broken ring where the spokes read as motion),
and pull the divider inset and the empty-folder "New chat in X" affordance
onto the same column as the sessions.

The alignment invariant test has been rewritten by each of the last two PRs
to accept the then-current misalignment. It now pins all three guides, adds
guide 3 at depth 2 with a nested fixture, and the gutter test pins the
gutter as out-of-flow and row-centred. Every number in the test header is a
measurement with its frame named: two paper estimates during this fix were
2px and 3px out.
CrysisDeu added a commit that referenced this pull request Aug 16, 2026
A folder in the session list and the sessions filed under it did not share a
left edge. #3766 added the row's status gutter as an in-flow flex child, so
its 12px width plus a gap landed on the content column: the agent label,
title and tool-call subtitle of every session inside a folder sat well right
of that folder's own name. #3903 then tightened the same rows and got
closer, but measured on a real render it still left the folder name 1px past
its sessions' text and a nested folder's glyph 2px short of their column.

Three guides now hold, in the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad. With
D = 2, a MEASURED container differential (the header's box starts 2px left
of the nested body's box, and no class in the component expresses it):

  guide 1  P = D + M                       14 = 2 + 12
  guide 2  P + G + g = D + M + B + p + R   33 = 33
  guide 3  P = R                           14 = 14

The status gutter moves out of the content flow (absolutely positioned
inside that pad) and is centred on the row rather than deriving the
headline's y; in flow it is what broke guide 2, and no class-level assertion
could see it. Because the algebra has no per-depth term it is scale-free:
depth 3 nests as depth 2 does, and root-lane sessions land on the root
folder's glyph, which restores the original pre-#1211 guide.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

This reverts #3903's row and folder geometry, agreed with its author. Its
collapsed-folder unread dot goes back inline on the right of the name rather
than into an absolute left gutter: that gutter is what forced the header pad
to 18px, and the pad is not free — it has to equal the session row's. The
2px colour bar from #3903 is kept, being orthogonal to the guides.

Also drop the title's mt-0.5 (row height 49.6 -> 43.6px idle, 68.1 -> 62.1
running), use lucide Loader for the running spinner (at 12px the
LoaderCircle arc reads as a broken ring where the spokes read as motion),
and pull the divider inset and the empty-folder "New chat in X" affordance
onto the same column as the sessions.

The alignment invariant test has been rewritten by each of the last two PRs
to accept the then-current misalignment. It now pins all three guides, adds
guide 3 at depth 2 with a nested fixture, and the gutter test pins the
gutter as out-of-flow and row-centred. Every number in the test header is a
measurement with its frame named: two paper estimates during this fix were
2px and 3px out.
CrysisDeu added a commit that referenced this pull request Aug 16, 2026
A folder in the session list and the sessions filed under it did not share a
left edge. #3766 added the row's status gutter as an in-flow flex child, so
its 12px width plus a gap landed on the content column: the agent label,
title and tool-call subtitle of every session inside a folder sat well right
of that folder's own name. #3903 then tightened the same rows and got
closer, but measured on a real render it still left the folder name 1px past
its sessions' text and a nested folder's glyph 2px short of their column.

Three guides now hold, in the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad. With
D = 2, a MEASURED container differential (the header's box starts 2px left
of the nested body's box, and no class in the component expresses it):

  guide 1  P = D + M                       14 = 2 + 12
  guide 2  P + G + g = D + M + B + p + R   33 = 33
  guide 3  P = R                           14 = 14

The status gutter moves out of the content flow (absolutely positioned
inside that pad) and is centred on the row rather than deriving the
headline's y; in flow it is what broke guide 2, and no class-level assertion
could see it. Because the algebra has no per-depth term it is scale-free:
depth 3 nests as depth 2 does, and root-lane sessions land on the root
folder's glyph, which restores the original pre-#1211 guide.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

This reverts #3903's row and folder geometry, agreed with its author. Its
collapsed-folder unread dot goes back inline on the right of the name rather
than into an absolute left gutter: that gutter is what forced the header pad
to 18px, and the pad is not free — it has to equal the session row's. The
2px colour bar from #3903 is kept, being orthogonal to the guides.

Also drop the title's mt-0.5 (row height 49.6 -> 43.6px idle, 68.1 -> 62.1
running), use lucide Loader for the running spinner (at 12px the
LoaderCircle arc reads as a broken ring where the spokes read as motion),
and pull the divider inset and the empty-folder "New chat in X" affordance
onto the same column as the sessions.

The alignment invariant test has been rewritten by each of the last two PRs
to accept the then-current misalignment. It now pins all three guides, adds
guide 3 at depth 2 with a nested fixture, and the gutter test pins the
gutter as out-of-flow and row-centred. Every number in the test header is a
measurement with its frame named: two paper estimates during this fix were
2px and 3px out.
bolichen97 pushed a commit that referenced this pull request Aug 16, 2026
A folder in the session list and the sessions filed under it did not share a
left edge. #3766 added the row's status gutter as an in-flow flex child, so
its 12px width plus a gap landed on the content column: the agent label,
title and tool-call subtitle of every session inside a folder sat well right
of that folder's own name. #3903 then tightened the same rows and got
closer, but measured on a real render it still left the folder name 1px past
its sessions' text and a nested folder's glyph 2px short of their column.

Three guides now hold, in the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad. With
D = 2, a MEASURED container differential (the header's box starts 2px left
of the nested body's box, and no class in the component expresses it):

  guide 1  P = D + M                       14 = 2 + 12
  guide 2  P + G + g = D + M + B + p + R   33 = 33
  guide 3  P = R                           14 = 14

The status gutter moves out of the content flow (absolutely positioned
inside that pad) and is centred on the row rather than deriving the
headline's y; in flow it is what broke guide 2, and no class-level assertion
could see it. Because the algebra has no per-depth term it is scale-free:
depth 3 nests as depth 2 does, and root-lane sessions land on the root
folder's glyph, which restores the original pre-#1211 guide.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

This reverts #3903's row and folder geometry, agreed with its author. Its
collapsed-folder unread dot goes back inline on the right of the name rather
than into an absolute left gutter: that gutter is what forced the header pad
to 18px, and the pad is not free — it has to equal the session row's. The
2px colour bar from #3903 is kept, being orthogonal to the guides.

Also drop the title's mt-0.5 (row height 49.6 -> 43.6px idle, 68.1 -> 62.1
running), use lucide Loader for the running spinner (at 12px the
LoaderCircle arc reads as a broken ring where the spokes read as motion),
and pull the divider inset and the empty-folder "New chat in X" affordance
onto the same column as the sessions.

The alignment invariant test has been rewritten by each of the last two PRs
to accept the then-current misalignment. It now pins all three guides, adds
guide 3 at depth 2 with a nested fixture, and the gutter test pins the
gutter as out-of-flow and row-centred. Every number in the test header is a
measurement with its frame named: two paper estimates during this fix were
2px and 3px out.
iamwhatever pushed a commit that referenced this pull request Aug 16, 2026
The sidebar's three folder alignment guides have regressed four times
(#1211, #3766, #3903, and twice on paper inside #3905), every time via a
Tailwind-class derivation that omitted the 2px FOLDER_BODY_INSET_PX
container differential between the folder header's box and the nested
body's box. The jsdom test (ChatSidebar.folderAlignment.test.tsx) cannot
catch that class of break: jsdom has no layout engine, so it asserts
class tokens and an arithmetic identity between constants, never the
rendered geometry.

Add a Playwright spec to the `E2E (stub ACP backend, offline)` gate that
seeds one folder, one subfolder, and sessions in both plus one ungrouped
session via the harness gateway API, then asserts the guides on
getBoundingClientRect().left of the real rendered elements:

- guide 1: folder glyph on its border-l connector line (depth 1 and 2)
- guide 2: folder name on the agent label / title of its sessions
  (depth 1 and 2)
- guide 3: nested folder glyph on its sibling sessions' content column,
  and the root-lane identity (ungrouped content on the root glyph)
- depth invariance: the depth-2 connector on depth 1's content column

Falsified against the historical regression class: rebuilding with
FOLDER_BODY_INSET_PX bumped 2 -> 4 fails the spec with
"fGlyph=259 vs fConnector=261" while the jsdom test stays green. The
missing-selector diagnostics were falsified the same way (a broken
selector fails with "missing: [aAgent, ...]", not a bare timeout).

Pre-push review (GPT + Opus model-pinned): no blocking. Fixed advisory
findings: absolute sanity anchors so difference-only assertions cannot
hold vacuously at left=0; seeding requests assert response.ok() so a
4xx fails as a seeding fault, not an alignment fault; the settle poll
subsumes the render wait (budget fits the 30s test timeout) and names
missing selectors; the serial-run dependency on the wipe discipline in
session-tags-folders.spec.ts is documented in the header.

The tool-call subtitle is not asserted: it needs a live agent turn to
render, is a sibling of the title inside the same block container (equal
left by construction), and its class parity is pinned by the jsdom test.

MIN_EXECUTED_SPECS raised 219 -> 220 for the added spec.

Closes #3914
iamwhatever pushed a commit that referenced this pull request Aug 16, 2026
The sidebar's three folder alignment guides have regressed four times
(#1211, #3766, #3903, and twice on paper inside #3905), every time via a
Tailwind-class derivation that omitted the 2px FOLDER_BODY_INSET_PX
container differential between the folder header's box and the nested
body's box. The jsdom test (ChatSidebar.folderAlignment.test.tsx) cannot
catch that class of break: jsdom has no layout engine, so it asserts
class tokens and an arithmetic identity between constants, never the
rendered geometry.

Add a Playwright spec to the `E2E (stub ACP backend, offline)` gate that
seeds one folder, one subfolder, and sessions in both plus one ungrouped
session via the harness gateway API, then asserts the guides on
getBoundingClientRect().left of the real rendered elements:

- guide 1: folder glyph on its border-l connector line (depth 1 and 2)
- guide 2: folder name on the agent label / title of its sessions
  (depth 1 and 2)
- guide 3: nested folder glyph on its sibling sessions' content column,
  and the root-lane identity (ungrouped content on the root glyph)
- depth invariance: the depth-2 connector on depth 1's content column

Falsified against the historical regression class: rebuilding with
FOLDER_BODY_INSET_PX bumped 2 -> 4 fails the spec with
"fGlyph=259 vs fConnector=261" while the jsdom test stays green. The
missing-selector diagnostics were falsified the same way (a broken
selector fails with "missing: [aAgent, ...]", not a bare timeout).

Pre-push review (GPT + Opus model-pinned): no blocking. Fixed advisory
findings: absolute coherence anchors so difference-only assertions cannot
hold vacuously at left=0; seeding requests assert response.ok() so a
4xx fails as a seeding fault, not an alignment fault; the settle poll
subsumes the render wait (budget fits the 30s test timeout) and names
missing selectors; the serial-run dependency on the wipe discipline in
session-tags-folders.spec.ts is documented in the header.

The tool-call subtitle is not asserted: it needs a live agent turn to
render, is a sibling of the title inside the same block container (equal
left by construction), and its class parity is pinned by the jsdom test.

MIN_EXECUTED_SPECS raised 219 -> 220 for the added spec.

Closes #3914
bolichen97 pushed a commit that referenced this pull request Aug 16, 2026
… (#3942)

The sidebar's three folder alignment guides have regressed four times
(#1211, #3766, #3903, and twice on paper inside #3905), every time via a
Tailwind-class derivation that omitted the 2px FOLDER_BODY_INSET_PX
container differential between the folder header's box and the nested
body's box. The jsdom test (ChatSidebar.folderAlignment.test.tsx) cannot
catch that class of break: jsdom has no layout engine, so it asserts
class tokens and an arithmetic identity between constants, never the
rendered geometry.

Add a Playwright spec to the `E2E (stub ACP backend, offline)` gate that
seeds one folder, one subfolder, and sessions in both plus one ungrouped
session via the harness gateway API, then asserts the guides on
getBoundingClientRect().left of the real rendered elements:

- guide 1: folder glyph on its border-l connector line (depth 1 and 2)
- guide 2: folder name on the agent label / title of its sessions
  (depth 1 and 2)
- guide 3: nested folder glyph on its sibling sessions' content column,
  and the root-lane identity (ungrouped content on the root glyph)
- depth invariance: the depth-2 connector on depth 1's content column

Falsified against the historical regression class: rebuilding with
FOLDER_BODY_INSET_PX bumped 2 -> 4 fails the spec with
"fGlyph=259 vs fConnector=261" while the jsdom test stays green. The
missing-selector diagnostics were falsified the same way (a broken
selector fails with "missing: [aAgent, ...]", not a bare timeout).

Pre-push review (GPT + Opus model-pinned): no blocking. Fixed advisory
findings: absolute coherence anchors so difference-only assertions cannot
hold vacuously at left=0; seeding requests assert response.ok() so a
4xx fails as a seeding fault, not an alignment fault; the settle poll
subsumes the render wait (budget fits the 30s test timeout) and names
missing selectors; the serial-run dependency on the wipe discipline in
session-tags-folders.spec.ts is documented in the header.

The tool-call subtitle is not asserted: it needs a live agent turn to
render, is a sibling of the title inside the same block container (equal
left by construction), and its class parity is pinned by the jsdom test.

MIN_EXECUTED_SPECS raised 219 -> 220 for the added spec.

Closes #3914

Co-authored-by: Joe Guo <zejiangg@amazon.com>
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…dotdev#3905)

A folder in the session list and the sessions filed under it did not share a
left edge. kirodotdev#3766 added the row's status gutter as an in-flow flex child, so
its 12px width plus a gap landed on the content column: the agent label,
title and tool-call subtitle of every session inside a folder sat well right
of that folder's own name. kirodotdev#3903 then tightened the same rows and got
closer, but measured on a real render it still left the folder name 1px past
its sessions' text and a nested folder's glyph 2px short of their column.

Three guides now hold, in the root lane and at every nesting depth:

  1. a folder glyph sits on its own border-l connector line
  2. a folder name shares one edge with the agent label / title /
     tool-call subtitle of every session inside it
  3. a NESTED folder's glyph sits on the content column of the sessions
     filed beside it, so a subfolder reads as their peer

Solving all three simultaneously forces header pad == session row pad. With
D = 2, a MEASURED container differential (the header's box starts 2px left
of the nested body's box, and no class in the component expresses it):

  guide 1  P = D + M                       14 = 2 + 12
  guide 2  P + G + g = D + M + B + p + R   33 = 33
  guide 3  P = R                           14 = 14

The status gutter moves out of the content flow (absolutely positioned
inside that pad) and is centred on the row rather than deriving the
headline's y; in flow it is what broke guide 2, and no class-level assertion
could see it. Because the algebra has no per-depth term it is scale-free:
depth 3 nests as depth 2 does, and root-lane sessions land on the root
folder's glyph, which restores the original pre-kirodotdev#1211 guide.

Measured on the built SPA: depth 1 glyph/connector 259, name and all three
text lines 278; depth 2 glyph/connector 278 (== depth 1's content column),
name/content 297; root lane content 259.

This reverts kirodotdev#3903's row and folder geometry, agreed with its author. Its
collapsed-folder unread dot goes back inline on the right of the name rather
than into an absolute left gutter: that gutter is what forced the header pad
to 18px, and the pad is not free — it has to equal the session row's. The
2px colour bar from kirodotdev#3903 is kept, being orthogonal to the guides.

Also drop the title's mt-0.5 (row height 49.6 -> 43.6px idle, 68.1 -> 62.1
running), use lucide Loader for the running spinner (at 12px the
LoaderCircle arc reads as a broken ring where the spokes read as motion),
and pull the divider inset and the empty-folder "New chat in X" affordance
onto the same column as the sessions.

The alignment invariant test has been rewritten by each of the last two PRs
to accept the then-current misalignment. It now pins all three guides, adds
guide 3 at depth 2 with a nested fixture, and the gutter test pins the
gutter as out-of-flow and row-centred. Every number in the test header is a
measurement with its frame named: two paper estimates during this fix were
2px and 3px out.
encomjp pushed a commit to encomjp/kirocrew-customapi that referenced this pull request Aug 22, 2026
…dotdev#3914) (kirodotdev#3942)

The sidebar's three folder alignment guides have regressed four times
(kirodotdev#1211, kirodotdev#3766, kirodotdev#3903, and twice on paper inside kirodotdev#3905), every time via a
Tailwind-class derivation that omitted the 2px FOLDER_BODY_INSET_PX
container differential between the folder header's box and the nested
body's box. The jsdom test (ChatSidebar.folderAlignment.test.tsx) cannot
catch that class of break: jsdom has no layout engine, so it asserts
class tokens and an arithmetic identity between constants, never the
rendered geometry.

Add a Playwright spec to the `E2E (stub ACP backend, offline)` gate that
seeds one folder, one subfolder, and sessions in both plus one ungrouped
session via the harness gateway API, then asserts the guides on
getBoundingClientRect().left of the real rendered elements:

- guide 1: folder glyph on its border-l connector line (depth 1 and 2)
- guide 2: folder name on the agent label / title of its sessions
  (depth 1 and 2)
- guide 3: nested folder glyph on its sibling sessions' content column,
  and the root-lane identity (ungrouped content on the root glyph)
- depth invariance: the depth-2 connector on depth 1's content column

Falsified against the historical regression class: rebuilding with
FOLDER_BODY_INSET_PX bumped 2 -> 4 fails the spec with
"fGlyph=259 vs fConnector=261" while the jsdom test stays green. The
missing-selector diagnostics were falsified the same way (a broken
selector fails with "missing: [aAgent, ...]", not a bare timeout).

Pre-push review (GPT + Opus model-pinned): no blocking. Fixed advisory
findings: absolute coherence anchors so difference-only assertions cannot
hold vacuously at left=0; seeding requests assert response.ok() so a
4xx fails as a seeding fault, not an alignment fault; the settle poll
subsumes the render wait (budget fits the 30s test timeout) and names
missing selectors; the serial-run dependency on the wipe discipline in
session-tags-folders.spec.ts is documented in the header.

The tool-call subtitle is not asserted: it needs a live agent turn to
render, is a sibling of the title inside the same block container (equal
left by construction), and its class parity is pinned by the jsdom test.

MIN_EXECUTED_SPECS raised 219 -> 220 for the added spec.

Closes kirodotdev#3914

Co-authored-by: Joe Guo <zejiangg@amazon.com>
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.

2 participants