Found while planning #721; filed, not fixed there.
sessions.handlers.ts:1165 synthesizes the legacy toolUseId for a screen-detected question as screen:${sessionId}:${key.length} — the length of the content key, not the key.
Two different questions whose content keys happen to have the same length share a toolUseId; probe row 7 hit it directly ([ ] Python and [✔] Python are both three characters apart, so both broadcasts carried the identical id).
Length is not identity.
Done looks like: the synthesized id derives from a digest of the key (or the registry's promptId where one exists), a test with two same-length different-content questions asserts distinct ids, and a mutation back to .length goes red.
Dedupe consumers on the legacy path (pendingQuestionKey, mobile toolUseId card keying) must be checked for reliance on the old value before changing it.
Found while planning #721; filed, not fixed there.
sessions.handlers.ts:1165synthesizes the legacytoolUseIdfor a screen-detected question asscreen:${sessionId}:${key.length}— the length of the content key, not the key.Two different questions whose content keys happen to have the same length share a
toolUseId; probe row 7 hit it directly ([ ] Pythonand[✔] Pythonare both three characters apart, so both broadcasts carried the identical id).Length is not identity.
Done looks like: the synthesized id derives from a digest of the key (or the registry's promptId where one exists), a test with two same-length different-content questions asserts distinct ids, and a mutation back to
.lengthgoes red.Dedupe consumers on the legacy path (
pendingQuestionKey, mobiletoolUseIdcard keying) must be checked for reliance on the old value before changing it.