From 46d0b95ee84fae0f79eee938c0e28cf7a57f3813 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 08:19:16 +0000 Subject: [PATCH] fix(pm): stop H5 title-login extraction at the login token (#9926) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit h5SeatStickerDesync took the WHOLE remainder after the emoji as the seat holder, so a consistent seat post like `๐ŸŸข os-warren (session_โ€ฆ)` mismatched its own assignee `[os-warren]` on every sweep โ€” the `(session_โ€ฆ)` parenthetical and any `ยท`-separated title suffix are display, not identity. Extraction now stops at the first whitespace-delimited token. Self-test: both named shapes (parenthetical-with-matching-assignee clean, parenthetical-with-no-assignee finding) plus reverse verification against the six titles pinned by the anchor sweep (#9857, run 32229942288) โ€” #7623/#6017/#6026/#9831 now clean, #6367/#6024 still flag. --- scripts/pm/check-half-states.mjs | 64 +++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/scripts/pm/check-half-states.mjs b/scripts/pm/check-half-states.mjs index 8c948f8bf3..8aa1708e2a 100644 --- a/scripts/pm/check-half-states.mjs +++ b/scripts/pm/check-half-states.mjs @@ -348,7 +348,15 @@ export function h5SeatStickerDesync(issue) { const status = m[2].trim(); const assignees = (issue.assignees ?? []).map((a) => a.login); if (status.startsWith('๐ŸŸข')) { - const holder = status.replace('๐ŸŸข', '').trim(); + // The login is only the FIRST whitespace-delimited token after the emoji. + // Everything past it โ€” the `(session_โ€ฆ)` parenthetical every active seat + // title carries by protocol, and any `ยท`-separated suffix (in-flight + // counts, queue depth, a body-edit timestamp) โ€” is display, not identity, + // and must never be compared against the assignee list (#9926: this used + // to take the WHOLE remainder as the holder, so a consistent seat post + // like `๐ŸŸข os-warren (session_โ€ฆ)` mismatched `[os-warren]` on every + // sweep). + const holder = status.replace('๐ŸŸข', '').trim().split(/\s+/u)[0] ?? ''; if (holder === 'Routine') return null; // Routine seats keep assignee empty by design if (!assignees.includes(holder)) { return `title says ๐ŸŸข ${holder} but assignees are [${assignees.join(', ') || 'none'}]`; @@ -1559,6 +1567,60 @@ function selfTest() { t('H5: ๐ŸŸข login without assignee -> finding', typeof h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] domain:devx โ€” ๐ŸŸข os-zhuang')), 'string'); t('H5: โณ vacant with assignee -> finding', typeof h5SeatStickerDesync(issue(['pm:seat'], ['os-help'], '', '[PM seat] domain:cli โ€” โณ vacant')), 'string'); t('H5: โณ vacant clean', h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] domain:cli โ€” โณ vacant')), null); + // #9926: the login-extraction fix. Both named shapes from the ruling. + t( + 'H5: ๐ŸŸข login with (session_โ€ฆ) parenthetical, matching assignee -> clean', + h5SeatStickerDesync(issue(['pm:seat'], ['os-x'], '', '[PM seat] domain:x โ€” ๐ŸŸข os-x (session_abc123)')), + null, + ); + t( + 'H5: ๐ŸŸข login with (session_โ€ฆ) parenthetical, no assignee -> finding', + typeof h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] domain:x โ€” ๐ŸŸข os-x (session_abc123)')), + 'string', + ); + // Reverse verification against the six titles pinned by the anchor sweep + // (#9857, run 32229942288) โ€” four measured false positives, then the two + // true positives, predicted direction first: clean, clean, clean, clean, + // finding, finding. + t( + 'H5 reverse-verify: #7623 (os-warren, consistent) -> clean', + h5SeatStickerDesync(issue(['pm:seat'], ['os-warren'], '', '[PM seat] skills โ€” ๐ŸŸข os-warren (session_01AeA3nU1B5Q2pgxqxgUrexd)')), + null, + ); + t( + 'H5 reverse-verify: #6017 (os-elon, consistent) -> clean', + h5SeatStickerDesync(issue(['pm:seat'], ['os-elon'], '', '[PM seat] domain:spec โ€” ๐ŸŸข os-elon (session_016D9wdJR14KKCxz1WgdAzcw)')), + null, + ); + t( + 'H5 reverse-verify: #6026 (os-zhuang, consistent) -> clean', + h5SeatStickerDesync(issue(['pm:seat'], ['os-zhuang'], '', '[PM seat] repo:cloud โ€” ๐ŸŸข os-zhuang (session_0137TnZzVmkSjXxoSVgPFS6S)')), + null, + ); + t( + 'H5 reverse-verify: #9831 (os-warren, consistent) -> clean', + h5SeatStickerDesync(issue(['pm:seat'], ['os-warren'], '', '[PM seat] repo:objectos โ€” ๐ŸŸข os-warren (session_01DXBoKN4MauvPdbMemPMqpr)')), + null, + ); + t( + 'H5 reverse-verify: #6367 (no assignee, ยท title suffix) -> finding', + typeof h5SeatStickerDesync( + issue(['pm:seat'], [], '', '[PM seat] domain:engine โ€” ๐ŸŸข os-elon (session_019yDEhPBC3tcGkW9bkce1HM) ยท ๅœจ้ฃž 1 ยท ้˜Ÿๅˆ— 0'), + ), + 'string', + ); + t( + 'H5 reverse-verify: #6024 (no assignee, session id with no login) -> finding', + typeof h5SeatStickerDesync( + issue( + ['pm:seat'], + [], + '', + '[PM seat] domain:cli โ€” ๐ŸŸข session_01WeN7F6jQFpcqW2BN56RdPa ยท ๅœจ้ฃž 2 ยท ้˜Ÿๅˆ— 1(ไธฒ่กŒ็ญ‰ไฝ) ยท ๅ†ณ็ญ–็ฎฑ 1 ยท ๆญฃๆ–‡ 2026-08-19 04:1xZ', + ), + ), + 'string', + ); t('H5: Routine seat needs no assignee', h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] ๅˆ†่ฏŠ โ€” ๐ŸŸข Routine')), null); t('H5: unparseable title -> finding', typeof h5SeatStickerDesync(issue(['pm:seat'], [], '', 'devx seat registry')), 'string'); t('H6: seat body over the soft bound -> finding', h6SeatBodyOversized(issue(['pm:seat'], [], 'x'.repeat(10_001), '[PM seat] domain:devx โ€” โณ vacant')), true);