Skip to content

Commit b40fe54

Browse files
os-warrenclaude
andauthored
fix(pm): stop H5 title-login extraction at the login token (#9926) (#9929)
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. Co-authored-by: Claude <noreply@anthropic.com>
1 parent 52db5df commit b40fe54

1 file changed

Lines changed: 63 additions & 1 deletion

File tree

scripts/pm/check-half-states.mjs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,15 @@ export function h5SeatStickerDesync(issue) {
348348
const status = m[2].trim();
349349
const assignees = (issue.assignees ?? []).map((a) => a.login);
350350
if (status.startsWith('🟢')) {
351-
const holder = status.replace('🟢', '').trim();
351+
// The login is only the FIRST whitespace-delimited token after the emoji.
352+
// Everything past it — the `(session_…)` parenthetical every active seat
353+
// title carries by protocol, and any `·`-separated suffix (in-flight
354+
// counts, queue depth, a body-edit timestamp) — is display, not identity,
355+
// and must never be compared against the assignee list (#9926: this used
356+
// to take the WHOLE remainder as the holder, so a consistent seat post
357+
// like `🟢 os-warren (session_…)` mismatched `[os-warren]` on every
358+
// sweep).
359+
const holder = status.replace('🟢', '').trim().split(/\s+/u)[0] ?? '';
352360
if (holder === 'Routine') return null; // Routine seats keep assignee empty by design
353361
if (!assignees.includes(holder)) {
354362
return `title says 🟢 ${holder} but assignees are [${assignees.join(', ') || 'none'}]`;
@@ -1559,6 +1567,60 @@ function selfTest() {
15591567
t('H5: 🟢 login without assignee -> finding', typeof h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] domain:devx — 🟢 os-zhuang')), 'string');
15601568
t('H5: ⏳ vacant with assignee -> finding', typeof h5SeatStickerDesync(issue(['pm:seat'], ['os-help'], '', '[PM seat] domain:cli — ⏳ vacant')), 'string');
15611569
t('H5: ⏳ vacant clean', h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] domain:cli — ⏳ vacant')), null);
1570+
// #9926: the login-extraction fix. Both named shapes from the ruling.
1571+
t(
1572+
'H5: 🟢 login with (session_…) parenthetical, matching assignee -> clean',
1573+
h5SeatStickerDesync(issue(['pm:seat'], ['os-x'], '', '[PM seat] domain:x — 🟢 os-x (session_abc123)')),
1574+
null,
1575+
);
1576+
t(
1577+
'H5: 🟢 login with (session_…) parenthetical, no assignee -> finding',
1578+
typeof h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] domain:x — 🟢 os-x (session_abc123)')),
1579+
'string',
1580+
);
1581+
// Reverse verification against the six titles pinned by the anchor sweep
1582+
// (#9857, run 32229942288) — four measured false positives, then the two
1583+
// true positives, predicted direction first: clean, clean, clean, clean,
1584+
// finding, finding.
1585+
t(
1586+
'H5 reverse-verify: #7623 (os-warren, consistent) -> clean',
1587+
h5SeatStickerDesync(issue(['pm:seat'], ['os-warren'], '', '[PM seat] skills — 🟢 os-warren (session_01AeA3nU1B5Q2pgxqxgUrexd)')),
1588+
null,
1589+
);
1590+
t(
1591+
'H5 reverse-verify: #6017 (os-elon, consistent) -> clean',
1592+
h5SeatStickerDesync(issue(['pm:seat'], ['os-elon'], '', '[PM seat] domain:spec — 🟢 os-elon (session_016D9wdJR14KKCxz1WgdAzcw)')),
1593+
null,
1594+
);
1595+
t(
1596+
'H5 reverse-verify: #6026 (os-zhuang, consistent) -> clean',
1597+
h5SeatStickerDesync(issue(['pm:seat'], ['os-zhuang'], '', '[PM seat] repo:cloud — 🟢 os-zhuang (session_0137TnZzVmkSjXxoSVgPFS6S)')),
1598+
null,
1599+
);
1600+
t(
1601+
'H5 reverse-verify: #9831 (os-warren, consistent) -> clean',
1602+
h5SeatStickerDesync(issue(['pm:seat'], ['os-warren'], '', '[PM seat] repo:objectos — 🟢 os-warren (session_01DXBoKN4MauvPdbMemPMqpr)')),
1603+
null,
1604+
);
1605+
t(
1606+
'H5 reverse-verify: #6367 (no assignee, · title suffix) -> finding',
1607+
typeof h5SeatStickerDesync(
1608+
issue(['pm:seat'], [], '', '[PM seat] domain:engine — 🟢 os-elon (session_019yDEhPBC3tcGkW9bkce1HM) · 在飞 1 · 队列 0'),
1609+
),
1610+
'string',
1611+
);
1612+
t(
1613+
'H5 reverse-verify: #6024 (no assignee, session id with no login) -> finding',
1614+
typeof h5SeatStickerDesync(
1615+
issue(
1616+
['pm:seat'],
1617+
[],
1618+
'',
1619+
'[PM seat] domain:cli — 🟢 session_01WeN7F6jQFpcqW2BN56RdPa · 在飞 2 · 队列 1(串行等位) · 决策箱 1 · 正文 2026-08-19 04:1xZ',
1620+
),
1621+
),
1622+
'string',
1623+
);
15621624
t('H5: Routine seat needs no assignee', h5SeatStickerDesync(issue(['pm:seat'], [], '', '[PM seat] 分诊 — 🟢 Routine')), null);
15631625
t('H5: unparseable title -> finding', typeof h5SeatStickerDesync(issue(['pm:seat'], [], '', 'devx seat registry')), 'string');
15641626
t('H6: seat body over the soft bound -> finding', h6SeatBodyOversized(issue(['pm:seat'], [], 'x'.repeat(10_001), '[PM seat] domain:devx — ⏳ vacant')), true);

0 commit comments

Comments
 (0)