Skip to content

fix(members): roster rows read like session rows, plus search - #6685

Merged
bolichen97 merged 1 commit into
mainfrom
fix/members-roster-preview-search
Aug 29, 2026
Merged

fix(members): roster rows read like session rows, plus search#6685
bolichen97 merged 1 commit into
mainfrom
fix/members-roster-preview-search

Conversation

@CrysisDeu

@CrysisDeu CrysisDeu commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

Problem / Motivation

PM review of the shipped Crew Members page (follow-up to #6616), three findings:

  1. Roster rows say Idle/Working — a label that repeats what the avatar presence dot already shows, while the useful line (what was last said in the thread, like every session row) is absent.
  2. The roster has no search box, unlike the sessions sidebar it is styled after.
  3. The detail drawer carries an X close button the chat page's side panel does not have — two close affordances teaching two gestures for the same job.

Why it matters

The members list and the sessions list are the product's two conversation surfaces. Every divergence between them is a pattern the user has to learn twice; the missing preview also makes the recency ordering unreadable (rows reorder between visits with no visible cause).

What changed

  • Rows: the status label is replaced by the thread's last-message preview — served from the roster endpoint via the same last_message_preview + credential/URL redaction path the sessions list uses, read in the same one-thread-hop batch as last_active_ts. Presence stays on the avatar dot.
  • Search: a SearchInput (sessions sidebar idiom) filters by name; new search_members key in all 12 locales, en-XA regenerated.
  • Sessions-list previews (behavioral fix, not just reuse): review surfaced that the shared preview path redacted AFTER the 120-char cap, so a credential straddling the boundary could leak its raw prefix. last_message_preview now takes an injected sanitize callable applied to the full text BEFORE the cap, and BOTH callers (sessions list + members roster) inject the shared redaction chain. Pinned by a boundary-straddling AKIA test.
  • Header/drawer: the thread header is name-only (like the chat header); the drawer's local close is now md:hidden — kept only below md where the overlay covers the header toggle, so desktop has exactly one close gesture.

Tests

  • Backend: roster row carries last_message ('' for a member with no transcript) — pinned in test_roster_reports_last_activity_from_the_dm_transcript.
  • Frontend: 14/14 — new pins for the preview sub-line, the absence of Idle/Working text, and the name filter (type → filtered, clear → restored).
  • i18n gates green (12,071 en-XA keys match, no shadowing); tsc clean.

Manual verification

Self-verified capture harness (assertions updated: no status labels, preview visible, exactly one search input), all frames green.

Screenshots / video

roster with previews and search
thread with drawer, no X on desktop

Mobile + light theme

mobile
mobile thread
light

no linked issue: PM chat feedback on the shipped page, no tracked issue filed.

@CrysisDeu
CrysisDeu requested a review from a team August 29, 2026 01:07
@CrysisDeu
CrysisDeu requested a review from a team as a code owner August 29, 2026 01:07
@CrysisDeu
CrysisDeu requested a review from dwu96 August 29, 2026 01:07
@github-actions github-actions Bot added the readiness: checking Automated validation is still running label Aug 29, 2026
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 6711caf4b0e917b433e516fb9cb9e16cfb95c9e9 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: PASS

Real UX-parity gap, solved in the right layers — and the redact-before-truncate fix it surfaced is the correct root-cause repair, applied to both callers.

Suggestions

  • last_message_preview(key, sanitize=None) leaves redaction opt-in on a primitive that feeds egress, and the two callers copy-paste an identical _sanitize closure — hoist it to one named helper beside redact_credentials and make the param required, so the leak class this PR just fixed (sanitization missing at the right point) can't recur via a third caller that passes nothing.
  • The sessions-preview redaction-order fix is a user-visible security behavior change riding a UX PR; it's well documented and tested here, but calling it out in the release notes when the version bumps (not just the PR body) is worth a note to the release owner.

[DESIGN-REVIEWED] 6711caf

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 6711caf4b0e917b433e516fb9cb9e16cfb95c9e9 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 6711caf

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

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — 🟡 CONCERNS

UX-level review of 6711caf4b0e917b433e516fb9cb9e16cfb95c9e9 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

UX-Verdict: CONCERNS

Swapping "Working/Idle" for previews leaves presence on an aria-hidden color dot alone — colorblind and screen-reader users lose the status the label used to carry.

Watch

  • Presence is now color-only and invisible to AT. The diff deletes the sub-line's only text encoding (status_working/status_idle, with a test pinning its absence) while the sole remaining signal is the 10px dot — aria-hidden="true", bg-ok green vs bg-muted/50 gray (MembersPage.tsx:254–259). Every roster view, persistent; colorblind users can't reliably split green from muted gray at that size, and screen readers hear nothing. Sessions rows, the stated model, still carry running as text/spinner. Fix: add sr-only presence text inside the row button (and a title on the dot).
  • The preview goes stale beside the live thread. last_message arrives once via api.members() on mount (MembersPage.tsx:97–114); WS updates only running. Message a member and the selected row's sub-line contradicts the pane next to it for the rest of the visit — every conversation, per-visit persistence, mild confusion. Fix: refresh the active row's preview on thread activity (or refetch the roster then).

Suggestions

  • A filter with no matches renders a silent blank list — the empty state is gated on members.length === 0 (MembersPage.tsx:220), not sortedMembers; add a one-line "No members match" row.

[UX-REVIEWED] 6711caf

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 6711caf4b0e917b433e516fb9cb9e16cfb95c9e9 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

All facts verified. I have what I need for the review: history.py already imports and uses the exact redaction pair in three other places, contradicting the docstring rationale for the injected sanitize callable; both callers pass an identical closure; the state.py sibling preview path already redacts before truncating; SearchInput is a genuinely shared component (30 files); isRunning keeps a consumer (the avatar dot).

First-Principles-Verdict: CONCERNS

The sanitize callable is a generalization for two identical consumers, justified by a claim (history.py never imports security) the file itself contradicts.

What this change ships

Intent: make the crew roster read and behave like the sessions list, per PM review of the shipped page — a FIX of three reported UX defects.

  1. Roster rows show last-message preview instead of Idle/Working — justified (named PM report)
  2. Roster gains a name search box — new capability riding in a fix, but declared, named report
  3. Thread header shows name only; status line deleted — justified subtraction
  4. Drawer X close hidden on desktop, kept below md — justified subtraction
  5. Sessions previews now redact before the 120-char cap — justified, cause-level leak fix
  6. Roster API row carries last_message — justified, 1 consumer (MembersPage sub-line)
  7. last_message_preview(sanitize=) parameter — two consumers, generalized; rationale contradicted
  8. members.py added to NON_EGRESS_REDACTION_MODULES — undeclared, but derived (existing registry)
  9. status_idle/status_working keys deleted, search_members added, 12 locales — derived (i18n invariant)
  10. Capture harness asserts the new layout — rides along, test infra

Watch

  • The docstring says the callable exists so "this module never imports the security layer" — but history.py:40 already imports redact_credentials/redact_exfiltration_urls and applies the pair at lines 1764, 7013, and 7157 (count: 3 existing in-module uses; grepped redact_credentials|redact_exfiltration_urls). The constraint the indirection protects does not exist.
  • sanitize=None fails open: a third caller that forgets the argument silently gets unredacted previews. Both real callers (count: 2 — members.py:166, sessions.py:1066) pass byte-identical closures, which is lens-5's "parameter every caller passes the same value for."
  • Sibling check ran clean: the other preview path (state.py:4567) already redacts before its 80-char truncate, so the leak fix leaves no unfixed sibling.

Subtractions

  • Drop the sanitize parameter from history.py:5298 and both _sanitize closures (members.py, sessions.py); call redact_exfiltration_urls + redact_credentials directly before the cap inside last_message_preview, using the import the module already has at history.py:40. Smaller surface, fail-closed for every future caller, deletes two duplicate closures.

[FIRST-PRINCIPLES-REVIEWED] 6711caf

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 6711caf4b0e917b433e516fb9cb9e16cfb95c9e9 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 6711caf

Verdict parsed from the review's SHA-scoped output markers for commit 6711caf4b0e917b433e516fb9cb9e16cfb95c9e9.

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

@CrysisDeu
CrysisDeu force-pushed the fix/members-roster-preview-search branch from 3f795bd to b78f2be Compare August 29, 2026 01:21
@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 29, 2026
@CrysisDeu

Copy link
Copy Markdown
Collaborator Author

GPT disposition — the truncation-before-redaction finding is valid and fixed in d0130f4, at the cause rather than by reverting the field:

Long text + credential -> truncation splits the credential -> its raw prefix reaches /api/members.

  • last_message_preview now accepts an injected sanitize callable applied to the FULL extracted text BEFORE the 120-char cap (a callable, so history.py still never imports the security layer).
  • Both callers inject the shared redaction chain: the new members roster AND handlers/sessions.py, which carried the identical pre-existing order-of-operations flaw this diff mirrored — fixing only the mirror would have left the same leak on the sessions surface.
  • Pinned by test_roster_preview_redacts_before_truncation: an AKIA token placed across the 120-char boundary; the response carries no AKIA prefix. 374 backend tests green.

@CrysisDeu
CrysisDeu force-pushed the fix/members-roster-preview-search branch from b78f2be to d0130f4 Compare August 29, 2026 01:31
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 29, 2026
Follow-up PM round on the Crew Members page (the prior batch landed
in #6616):

- Roster rows drop the Idle/Working label for the thread's
  last-message preview — the same sub-line a session row carries.
  Presence already rides the avatar dot, so the textual label said
  nothing the dot did not. The preview reuses the sessions list's
  last_message_preview + redaction path, so a member row can never
  leak what a session row would not.
- A SearchInput (the sessions sidebar idiom) filters the roster by
  name; new search_members key across 12 locales, en-XA regenerated.
- The thread header drops its status sub-line (name only, like the
  chat page header), and the drawer's local close button survives
  only below md — on desktop the header's panel toggle is the one
  close gesture, matching the chat page's side panel.

Backend: the roster response gains last_message, read in the same
one-thread-hop batch as last_active_ts.

Tests: backend pins last_message ('' when no transcript); frontend
pins the preview sub-line, the absence of status labels, and the
name filter. Capture assertions updated and all frames re-verified.
@CrysisDeu

Copy link
Copy Markdown
Collaborator Author

First Principles dispositions — addressed in 6711caf:

~20 formatting-only hunks in history.py ride along … zero-cost to drop, convention says own commit.

  • fixed — history.py was restored from origin/main and only the substantive change replayed: the diff is now 1 file, +12/−1, exactly the last_message_preview signature, docstring, and pre-cap sanitize call. The riders came from running black on a baseline-listed file; dropped per AGENTS.md's own-commit rule.

Item 6 is an undeclared behavioral fix to the sessions list … declare it.

  • fixed — the PR body's "What changed" now names the sessions-list behavioral fix explicitly (redaction moved before the cap, both callers inject the chain, boundary-straddling AKIA test pins it).

Item 2 … no named person failing to find a member; deferring search would still resolve findings 1 and 3.

  • rebutted — the search box is not inherited symmetry: it is an explicit item in the PM's review of the live page ("crew member 的 searchbar 呢"), i.e. a named user asking for exactly this affordance. The locale cost is one key, already translated across the 12 catalogs in this diff.

@CrysisDeu
CrysisDeu force-pushed the fix/members-roster-preview-search branch from d0130f4 to 6711caf Compare August 29, 2026 01:40
@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: action required A blocking check or review needs attention readiness: checking Automated validation is still running labels Aug 29, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) August 29, 2026 05:20

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent subagent code review completed for this head; no blocking changes found.

@bolichen97
bolichen97 merged commit 569621a into main Aug 29, 2026
67 checks passed
@bolichen97
bolichen97 deleted the fix/members-roster-preview-search branch August 29, 2026 05:52
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 29, 2026
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