Skip to content

fix(channels): drop stale channel-list responses and show the unread badge on mobile (#154, #155) - #225

Merged
dborup merged 4 commits into
masterfrom
codex/issue-154-155-channels-race-mobile-badge
Oct 5, 2026
Merged

dborup merged 4 commits into
masterfrom
codex/issue-154-155-channels-race-mobile-badge

Conversation

@dborup-agent

@dborup-agent dborup-agent commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Relates to #154, #155

Base. #153 and #166 are now merged into master, and master is merged into this branch. The diff against master is only this PR's own changes:

  • 3533318c: the tests;
  • 7fc8a7a9: the fix;
  • 33e8d1ad: review F1, an E2E click target (test only).

#154: overlapping loadChannels() calls

Problem. Every caller (region change, the show-encrypted toggle, shared-channel approval, init()) awaited api('/channels' + qs) and then wrote channels unconditionally. The response that arrived last won, not the request that was made last. A delayed SJC response could replace the SJC+SFO list while the pills still showed SJC+SFO.

Fix. The fix follows the messageRequestId / isStaleMessageRequest() pattern:

  • loadChannels() numbers its requests (channelsRequestId).
  • If a newer request has started when a response or failure arrives, that response or failure is dropped. It causes no render, no reconcileSelectionAfterChannelRefresh() and no "Failed to load channels".
  • The dropped call resolves together with the newest load (latestChannelsLoad). A caller's follow-up therefore runs against the list that is actually rendered. Examples are init()'s deep link selectChannel(routeParam) and the region handler's message refresh. Without this, a deep link whose first load is superseded would open before the newer list exists. It would get the header "Channel x — 0 messages", and the unit test pins this.
  • fix(channels): keep client-only state (PSK selection, My Channels, unread) across a channel-list refresh #152 compatibility: each request still samples wsActivitySeq when it starts, so mergeClientChannelState() runs with the sample of the request that renders.
  • No new /channels requests, and the callers are unchanged.

#155: unread badge on mobile rows

Problem. renderChannelRowMobile() (Kpa-clawbot#1367) never rendered ch.unread.

Fix.

  • The badge markup moves into renderUnreadBadge(ch), which the desktop row and the mobile row share. The desktop output is byte-identical; a test pins it.
  • On mobile the badge sits in .ch-row-line1 after the time. It keeps the 99+ cap, title and aria-label, and uses the existing .ch-unread-badge class. No CSS was changed and no new colours were added. The count and the hash are escaped.
  • Mobile rows re-render through renderChannelList() both when live decryption bumps the unread count and when a channel is opened (which resets unread to 0). So the badge updates and clears the same way as on desktop. No separate DOM patch path is needed.

test-channel-ux-followup.js evaluates renderChannelRow() alone in a sandbox. It now also loads the real renderUnreadBadge(), the same way it already loads channelDisplayName().

Tests

Unit tests are in test-channels-client-state-152.js. They load the real channels.js in the existing vm harness, and each /channels request gets its own deferred response.

Acceptance criterion Test Mutant (killed)
#154 delayed older response + newer region change: newer list renders and stays; no extra request #154: an older region response that lands last… (unit), #154 a late SJC response… (E2E desktop + 390 px, region=SJC held 2.5 s) M154-1 no stale check
#154 dropped response: no reconcile #154: a dropped older response does not reconcile… M154-1
#154 dropped failure: no error over the newer list #154: a dropped older failure… M154-3 no stale check in catch
#154 callers unchanged; deep link opens against the rendered list #154: a dropped call resolves only after the newest list rendered… M154-2 dropped call resolves at once
#154 compatible with the #152 merge #154: the #152 client-state merge still applies… (guard) –
#155 badge at mobile width, 99+, title, aria-label, none at 0 #155: a mobile row shows the unread badge… (unit), E2E 390 px M155-1 no mobile badge, M155-2 no cap
#155 live update and clear on open #155: on mobile a live-decrypted message… (unit, real WS handler + real decrypt); E2E injects live encrypted frames into the page's WebSocket (page.routeWebSocket) at 390 px and 1280 px M155-1, M155-3 open does not clear
#155 desktop unchanged #155: the desktop badge markup is unchanged (guard), E2E desktop control –

Perf

Not a hot path. loadChannels() adds one integer comparison per response. The badge is O(1) per row inside the existing renderChannelList() pass, so there is no new DOM rebuild. Nothing changes in the merge or the reconcile.

Not verified

  • Real devices and touch input; only Playwright viewports were tested.
  • Pre-existing and unchanged: a dropped region call's follow-up (refreshMessages / selectChannel) still runs once per region change, as before, so two quick changes still refresh the messages twice for the current region.

🤖 Generated with Claude Code

dborup and others added 2 commits October 4, 2026 11:24
…ing mobile unread badge (#154, #155)

Unit (test-channels-client-state-152.js, real channels.js in the vm
harness, api() timing controlled per request):
- #154: a late older region response must not replace the newer list,
  must not reconcile the selection away, and a stale failure must not
  paint "Failed to load channels"; a dropped init() load must resolve
  only after the newest list rendered, so a deep link opens against it.
  Guard: the #152 client-state merge still applies to the request that
  renders.
- #155: the Kpa-clawbot#1367 mobile row renders the unread badge (99+ cap, title,
  aria-label, none at 0); a live-decrypted message for a closed PSK
  channel bumps it and opening the channel clears it. Guard: desktop
  badge markup unchanged.
Harness: mobile option for matchMedia, the debounced WS batch handler is
captured, one fake child per querySelector selector, deferred() rejects.

E2E (test-channels-154-155-e2e.js): a held-back region=SJC response
lands after the SJC+SFO list (desktop and 390 px), and live PSK frames
injected through page.routeWebSocket at 390 px and 1280 px.

On the #166 head: unit 6 of the 8 new tests fail (the 2 guards pass);
E2E 3 of 8 steps fail (#154 desktop + mobile, #155 mobile badge).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…badge on mobile (#154, #155)

#154: loadChannels() now numbers its requests (channelsRequestId, like
messageRequestId). A response, or a failure, whose request is no longer
the newest is dropped: no render, no reconcile, no "Failed to load
channels". The dropped call resolves with the newest load, so a
caller's follow-up (init()'s deep link, the region handler) runs
against the list that actually renders. Each request still samples
wsActivitySeq when it starts, so the #152 merge uses the sample of the
request that renders. No extra /channels request, callers unchanged.

#155: the unread badge moves into renderUnreadBadge(), shared by the
desktop row (markup unchanged) and the Kpa-clawbot#1367 mobile row, where it sits
in line 1 after the time. Same 99+ cap, title and aria-label, escaped,
existing .ch-unread-badge styling. Mobile rows re-render through
renderChannelList() on the live unread bump and when a channel opens,
so the badge updates and clears there as on desktop.

test-channel-ux-followup.js evaluates renderChannelRow() alone in a
sandbox; it now loads the real renderUnreadBadge() next to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Report — CS-pve-agent1 PR#225 #154+#155 — head 7fc8a7a

Status: draft. Tests first (red on the #166 head), then the fix. Every acceptance criterion has a test, and each mutant is killed. Local unit and E2E green; CI: green (run 37199092116).

Stacked on #166 (c6c09d75), which is stacked on #153 (3ab81130). Merge order: #153 → #166 → #225. Commits: 3533318c (tests) and 7fc8a7a9 (fix).

Evidence tags: [T] = test run locally, [A] = analysis of code or diff, [K] = CI result.

Acceptance criteria

Issue Criterion Test Red on #166 head Mutant (survives the pre-existing suites, killed by)
#154 A delayed older response followed by a newer region change: the newer list renders and stays unit #154: an older region response that lands last…; E2E #154 a late SJC response… (route holds region=SJC for 2.5 s; desktop and 390 px) yes, unit and E2E desktop + mobile [T] M154-1 (stale check removed): killed by 3 unit tests and 2 E2E steps [T]
#154 A dropped response does not reconcile unit #154: a dropped older response does not reconcile… yes [T] M154-1 [T]
#154 A dropped failure does not paint "Failed to load channels" unit #154: a dropped older failure… yes [T] M154-3 (no stale check in catch): killed by 1 unit test [T]
#154 No extra /channels request, and callers unchanged request count asserted in unit and E2E (exactly 1 per pill click); unit #154: a dropped call resolves only after the newest list rendered… (init deep link) yes [T] M154-2 (dropped call resolves at once): killed by 1 unit test [T]
#154 Compatible with the #152 merge (sample of the rendering request) unit #154: the #152 client-state merge still applies… (guard) passes (guard) [T] –
#155 Badge at 390 px for unread > 0, 99+, title, aria-label, none at 0 unit #155: a mobile row shows the unread badge…; E2E 390 px (visible, inside the row and the viewport) yes [T] M155-1 (no mobile badge), M155-2 (no 99+ cap): each killed by 2 unit tests; M155-1 also by 1 E2E step [T]
#155 Live update and clear on open unit #155: on mobile a live-decrypted message… (real WS batch handler, real decrypt); E2E injects encrypted GRP_TXT frames into the page's WebSocket (page.routeWebSocket) yes, unit and E2E mobile [T] M155-3 (opening does not clear unread): killed by 1 unit test [T]
#155 Desktop unchanged unit #155: the desktop badge markup is unchanged (byte-identical guard); E2E desktop control passes (guard) [T] –

"Survives the pre-existing suites" means: sh test-all.sh with the #166 version of test-channels-client-state-152.js stays at 215/0 for every mutant [T]. On the first mutant pass, one file in test-all.sh and the existing F1/N1 tests (M154-2) failed while E2E suites were running in parallel. On reruns on an idle machine they were green, and 15 runs of the file each on #166 and on #225 gave 0 failures [T]. That is load-related timing in the Web Crypto settle() steps, and the code here is unchanged.

Local tests on 7fc8a7a9

Suite Result
sh test-all.sh 215/0 [T]
node test-frontend-helpers.js 707/0 [T]
test-channels-client-state-152.js 63/0 [T]
test-channels-154-155-e2e.js 8/0, 3 runs in a row [T]
test-channel-ux-followup.js 31/0 [T]
test-channels-client-state-152-e2e.js, -decrypt-e2e.js, test-channel-proposals-e2e.js 6/0, 18/0, 23/0 [T]
test-e2e-playwright.js 132/135, 3 skips (same as CI) [T]
The other channel E2E suites (same list as on #153/#166) all 0 failed [T]
scripts/check-xss-sinks.sh --diff (against master and against #166) clean [T]
scripts/check-css-vars.js OK; no CSS changed [T]
  • Fork guards are unchanged: deploy.yml 9, release-fast-path.yml 1. The only deploy.yml change is one Playwright line for the new E2E. [A]
  • Browser check: Chromium screenshots at 390 px and 1280 px after 3 live frames show the PSK row with a "3" badge on mobile (to the right of the time), and the desktop row unchanged. [T]

CI (run 37199092116, head 7fc8a7a9, conclusion: success)

Job Result
✅ Go Build & Test success. test-all.sh: 215 passed, 0 failed; test-channels-client-state-152.js 63/0; test-channel-ux-followup.js 31/0; the preflight XSS --diff step passed [K]
🎭 Playwright E2E Tests success. test-channels-154-155-e2e.js 8/0 (all 8 #154/#155 steps ✓); test-e2e-playwright.js 132/135 (3 skips); test-channels-client-state-152-e2e.js 6/0; test-channel-proposals-e2e.js 23/0; test-channels-client-state-152-decrypt-e2e.js 18/0 [K]
🏗️ Build & Publish Docker Image success [K]
📦 Release Artifacts, 🚀 Deploy Staging, 📝 Publish Badges & Summary skipped (fork guards / PR event) [K]

Remaining items

  • Unchanged existing behaviour: each region change still runs its follow-up message refresh. Two quick changes therefore still refresh the current region's messages twice; that does not cause an extra /channels request. [A]
  • Not verified on real devices or with touch input. [A]

🤖 Generated with Claude Code

@adminopenclaw8-sketch

Copy link
Copy Markdown
Collaborator

Review — CS-Minimax PR#225 kanalpakke — head 7fc8a7a

Dom: APPROVE med nits

Independent, read-only review of the channel stack #153 → #166 → #225.

Evidence tags: [T] run here, [A] analysis of the source, [K] taken from the author's report or CI, not re-run.

Findings (#225)

# Severity Finding Evidence
F1 should-fix, test only The desktop step #155 opening the PSK channel clears the badge in test-channels-154-155-e2e.js fails 5 of 5 runs locally (macOS Chromium, 1280×800), though CI on Linux is green.
- With the badge, the My Channels PSK row wraps to 109 px (92 px without it).
- page.click() on the row clicks its centre, which then lands on the Share button (.ch-share-btn). That opens the share dialog instead of selecting the channel.
- Master's desktop row renders the same badge markup and wraps the same way (109 px; the centre hits Share). So this is a font-dependent click target in the new test, not a product regression.
- Fix: click .ch-item-name / .ch-row-name instead of the row.
[T]
F2 info, pre-existing At 1280 px a desktop My Channels row with name, key icon, unread badge, colour dot, Share and the 48 px Remove button wraps onto three lines. This is cosmetic and identical on master. [T]
F3 info The author already notes this one: each region change still runs its follow-up message refresh, so two quick changes refresh the current channel twice. That causes no extra /channels request. [A][K]

#154: overlapping loadChannels()

  • Pattern. loadChannels() stamps ++channelsRequestId and keeps latestChannelsLoad, following the messageRequestId pattern.
    • The stale check sits right after the only await (api('/channels'…)), in both the success and the catch path.
    • There is no further await between the check and renderChannelList() / reconcileSelectionAfterChannelRefresh(), so an older response cannot slip through after the check.
    • A dropped call returns the newest load's promise, so callers' follow-ups (the init() deep link, the region handler) run against the list that renders. No promise can resolve to itself: only a request that is already stale returns latestChannelsLoad, and that is always a later request's promise. [A]
  • Compatible with fix(channels): keep client-only state (PSK selection, My Channels, unread) across a channel-list refresh #153's merge. seqAtRequestStart is sampled per request before its await, so the request that renders uses its own sample.
    • The unit guard #154: the #152 client-state merge still applies… is green. [T]
    • Mutant Mu154c (sample replaced by 0) is killed by the clock-skew tests. [T]
  • Browser, own probe with a Playwright route holding region=SJC for 2.5 s. I clicked SJC, then SFO, then SJC again to deselect it, which leaves SFO only. The final region is SFO. [T]
  • No extra requests. The probe above and the PR's own E2E both count one /api/channels request per pill click. [T]

#155: unread badge on mobile rows

  • Shared helper. renderUnreadBadge(ch) is used by the desktop and the mobile row.
    • The count (String(ch.unread)) and the hash are escapeHtml'd.
    • It carries title="N new" and aria-label="N unread", and the visible text is capped at 99+.
    • It uses the existing .ch-unread-badge class (background: var(--accent, …)). No CSS changed and no new colour was added. [A]
  • Desktop unchanged. The desktop output is identical for numeric counts: the leading space moved from the helper into the template. The unit guard #155: the desktop badge markup is unchanged is green. [T][A]
  • Browser, 390×844, own probe.
    • After 120 live-decrypted frames for a closed PSK channel, injected with routeWebSocket (synthetic test key only), the badge reads 99+, title="120 new", aria-label="120 unread".
    • It sits in .ch-row-line1 after the time and inside both the row and the viewport.
    • Opening the channel clears it. [T]
    • A screenshot was taken and checked: the badge sits to the right of "0s ago" on the PSK row.
  • Live updates. Unread is bumped in the WS handler, followed by renderChannelList(), so mobile rows update with no separate patch path. Selecting a channel zeroes unread before its renderChannelList(). [A][T]
  • Escaping, own probe. A PSK label <img src=x onerror=…> and a decrypted message containing <b>/<img onerror> render as text on desktop and mobile. There are 0 img elements in the row and nothing ran. [T]

Shared test section (all three PRs)

Unit suites per merged tree

Merged tree sh test-all.sh node test-frontend-helpers.js test-channels-client-state-152.js test-channel-decrypt-m345.js test-channel-ux-followup.js test-channel-proposals.js test-channels-merge-1498-unit.js
#153 0f293e20 215/0 707/0 42/0 24/0 30/0 32/0 12/0
#166 37264b07 215/0 707/0 55/0 24/0 30/0 32/0 12/0
#225 6d4b7760 215/0 707/0 63/0 24/0 31/0 32/0 12/0

All [T].

E2E

The server was a local Go server built from the #225 merged tree; the PRs change no Go code. It ran on e2e-fixture.db, prepared as in CI: freshen, the inline Kpa-clawbot#1486/Kpa-clawbot#1791 seed from deploy.yml, corescope-migrate, seed-2073, seed-199. Each tree's public/ was served on its own port, and every server was stopped by its port-owner pid.

Tree Suite Result
#225 test-channels-154-155-e2e.js 7/1: only the desktop "opening clears" step fails, 5 of 5 reruns (F1) [T]
#225 test-channels-client-state-152-e2e.js 6/0 [T]
#225 test-channels-client-state-152-decrypt-e2e.js (own server + ingestor) 18/0 [T]
#225 test-channel-proposals-e2e.js (own server + ingestor, incl. the #196 auto-approve steps) 23/0 [T]
#225 test-channel-decrypt-e2e.js, test-channel-issue-1087-e2e.js, test-channel-issue-1111-e2e.js 15/0, 3/0, 2/0 [T]
#225 test-channels-{list-render,selection-flow,add-modal,ws-batch,share-color,observed-path-hash-size}-e2e.js, test-channels-ws-race-1498-e2e.js 8/0, 8/0, 8/0, 6/0, 6/0, 7/0, 5/0 [T]
#225 test-channel-{modal,fluid,qr,color-picker}-e2e.js 15/0, 15/0, 11/0, 10/0 [T]
#225 test-e2e-playwright.js stops (fail-fast) at a non-channel test, Version info lives on Perf dashboard (navStats wait), and stops the same way on master. A scratch copy without fail-fast gave 130 passed / 1 failed / 8 skipped on #225 and exactly the same on master: the same single failure, a local environment issue. [T] CI: 132/135. [K]
#153 152-e2e, 1111, selection-flow, channel-decrypt-e2e 6/0, 2/0, 8/0, 15/0 [T]
#166 152-e2e, 1111, selection-flow, channel-decrypt-e2e 6/0, 2/0, 8/0, 15/0 [T]
#166 #225's test-channels-154-155-e2e.js (before the fix) 4/4: both #154 steps and the mobile #155 badge step fail as expected, plus F1's desktop step [T]

Own browser probes (#225 merged tree vs master)

Probe #225 master
SJC held 2.5 s, then SFO only SFO list stays; 3 requests for 3 clicks list replaced by the SJC set
PSK conversation open, then region change, then show-encrypted on (desktop) URL, header ("Review Probe — 0 messages"), selection and the labelled PSK row all kept conversation closed: "Select a channel", URL #/channels, PSK row gone
Unread 3 on the PSK row, then region change, then show-encrypted off badge stays 3 not reached; the row was already gone

All [T]. The show-encrypted toggle was driven the way the customizer does it: the localStorage key plus mc-channels-show-encrypted-changed.

Mutants

Each mutant was applied to a copy of the #225 merged tree and run against test-channels-client-state-152.js, plus the file listed for it.

# Issue Mutant Result
Mu152a #152 no mergeClientChannelState() in the load killed (56/7)
Mu152b #152 unread not carried killed (58/5)
Mu152c #152 reconcile before the merges killed (51/12)
Mu152d #152 no mergeUserChannels() in the load killed (48/15), incl. the region, show-encrypted and onApproved trigger tests
Mu163a #163 no stale check in the stored-key loop killed (60/3)
Mu163b #163 raw channel part in the cache key killed (60/3); test-channel-decrypt-m345.js stays 24/0
Mu163c #163 no init-time ensureCacheMigrated() killed (61/2)
Mu163d #163 clearChannelCache with the raw prefix killed (62/1)
Mu154a #154 no stale check after the api() await killed (60/3), and in the browser race probe (list replaced by SJC)
Mu154b #154 dropped call resolves at once killed (62/1, deep-link test)
Mu154c #154 WS sequence sample replaced by 0 killed (61/2, clock-skew tests)
Mu155a #155 no badge on mobile rows killed (61/2); test-channel-ux-followup.js stays 31/0
Mu155b #155 title dropped from the badge killed (61/2, incl. the desktop guard)
Mu155c #155 opening a channel does not clear unread killed (62/1)

All [T].

Security, performance and rules (all three)

Not verified

  • Real devices and touch input; Playwright viewports only.
  • The approval trigger in a browser with a PSK open. It is covered by the unit test (shared-channel approval (onApproved) keeps the open PSK conversation, killed by Mu152d), and the proposals E2E (23/0) covers approval itself.
  • Linux font metrics for F1; CI is green there. [K]
  • Browsers other than Chromium.

The heads were 3ab81130…, c6c09d75… and 7fc8a7a9… both before and after this review (git ls-remote). All three PRs are still drafts and were not modified.


Generated by Claude Code

dborup and others added 2 commits October 4, 2026 13:25
Master now contains #153 and #166, so the diff against master is this
PR's own files only. No conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iew F1)

page.click() on a channel row aims at the row's centre. Where a desktop
My Channels row wraps (font metrics; the unread badge makes the row
wider) that centre is the Share button or the colour dot, so the
"opening clears the badge" step opened the share dialog or the colour
picker instead of the channel (5/5 on macOS Chromium at 1280x800).

Both row clicks now go through openChannel(), which clicks
.ch-item-name / .ch-row-name and waits for the channel URL. Test only;
production code unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dborup-agent

Copy link
Copy Markdown
Collaborator Author

Report — CS-pve-agent1 PR#225 runde 2 — head 33e8d1a

Status: F1 fixed in the test only, master merged without conflicts, the fixed E2E passed 5 of 5 runs, local suites are green, CI: green (run 37205823872). Still a draft.

Review feedback addressed (commit 33e8d1ad)

Evidence tags: [T] = test run locally, [A] = analysis of code or diff, [K] = CI result.

F1: E2E click target (should-fix, test only)

Cause. page.click() on a channel row aims at the row's centre. When a desktop My Channels row wraps, that centre can be a control inside the row instead of the row body. The wrap depends on font metrics, and the unread badge makes the row wider.

I reproduced it on Linux by enlarging .ch-item-name in temporary copies of the test (not committed) [T]:

Name font PSK row height Element at row centre Old test, desktop "opening clears" step Fixed test
default 80–94 px .ch-row-body / .ch-item-top pass pass
20 px 113 px .ch-share-btn pass here (the click still reached the row's select handler) pass
28 px 202 px .ch-color-dot fail: the colour picker opens and the channel URL never comes (same failure as reported on macOS) pass

Fix (33e8d1ad).

  • A new helper, openChannel(page, hash), clicks .ch-item-name / .ch-row-name inside the row and waits for #/channels/<hash>.
  • Both row clicks in the file use it: opening #test during setup, and opening the PSK channel in the "clears" step. This covers desktop and mobile.
  • The file's other clicks go to buttons (the region pills, .ch-back, Add and the modal's Add) and need no change.
  • Production code is unchanged. [A]

F2 and F3

  • F2 (a wrapped desktop row with every control): not changed. It is pre-existing and cosmetic, and identical on master. [A]
  • F3 (one follow-up message refresh per region change): not changed. It was already noted in the PR as existing behaviour, and it causes no extra /channels request. [A]

Master merge

Tests on 33e8d1ad

The local Go server ran on a copy of e2e-fixture.db, prepared the way CI does it: freshen, the inline seed, corescope-migrate, then seed-2073 and seed-199. It was stopped by the pid of the process that owned the port.

Suite Result
test-channels-154-155-e2e.js 8/0 in 5 of 5 runs [T]
sh test-all.sh 215 passed, 0 failed [T]
node test-frontend-helpers.js 707/0 [T]
test-channels-client-state-152.js 63/0 [T]
test-channels-client-state-152-e2e.js, -decrypt-e2e.js 6/0, 18/0 [T]
test-channel-proposals-e2e.js 23/0 [T]
test-e2e-playwright.js 132/135, 3 skips (same as CI) [T]
The other channel E2E suites: test-channel-{decrypt,fluid,issue-1087,issue-1111,modal,qr,color-picker}-e2e.js, test-channels-{add-modal,list-render,observed-path-hash-size,selection-flow,share-color,ws-batch,ws-race-1498}-e2e.js, test-issue-1224-…, test-issue-1367-… all 0 failed [T]
scripts/check-xss-sinks.sh --diff origin/master clean [T]

CI (run 37205823872, head 33e8d1ad, conclusion: success)

Job Result
✅ Go Build & Test success. test-all.sh: 215 passed, 0 failed; test-channels-client-state-152.js 63/0; test-channel-ux-followup.js 31/0; the preflight XSS --diff step passed [K]
🎭 Playwright E2E Tests success. test-channels-154-155-e2e.js 8/0, with all 8 steps ✓ including desktop "opening the PSK channel clears the badge"; test-e2e-playwright.js 132/135 (3 skips); test-channels-client-state-152-e2e.js 6/0; test-channel-proposals-e2e.js 23/0; test-channels-client-state-152-decrypt-e2e.js 18/0 [K]
🏗️ Build & Publish Docker Image success [K]
📦 Release Artifacts, 🚀 Deploy Staging, 📝 Publish Badges & Summary skipped (fork guards / PR event) [K]

Remaining items

  • F2 and F3, as above.
  • Only Linux Chromium was run here. The macOS case was reproduced by forcing the wrap, not on a Mac. [A]
  • Real devices and touch input were not tested.

🤖 Generated with Claude Code

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.

3 participants