Problem
PrefixId(x) where x is already a PrefixId collapses its .full to the 8-char short form, because str.__new__(PrefixId, a_prefixid) routes through PrefixId.__str__ (which returns .short) rather than copying the full underlying buffer.
Repro:
from cc_explorer.utils import PrefixId
full = "f236adae-550d-47c0-8572-e7f8e88a4ee9"
a = PrefixId(full)
b = PrefixId(a) # double-wrap
assert a.full == full # ok (36 chars)
assert b.full == full # FAILS — b.full == "f236adae" (8 chars)
Impact
SessionInfo.session_id (and the new _ArtifactSession.session_id) are already PrefixIds. Several resolvers re-wrap them before comparing:
mcp_server.py _resolve_unique_session — PrefixId(s.session_id) == session
mcp_server.py (~line 891) — PrefixId(s.session_id) == sid
mcp_server.py _resolve_artifacts_corpus — PrefixId(s.session_id) == raw_id
Because the wrapped side collapses to 8 chars, equality degrades to an 8-char prefix match. A caller that supplies a >8-char disambiguating prefix to pick one of two sessions sharing the same first 8 chars gets a spurious "ambiguous" error (or, in the unique path, a match that ignored the extra disambiguating chars). Full-id and ≤8-char-prefix lookups are unaffected, so it's latent.
The fix is to drop the redundant re-wrap (s.session_id == raw_id) since s.session_id is already a PrefixId whose __eq__ does prefix matching on the full value. Consider also guarding PrefixId.__new__ against re-wrapping a PrefixId so this can't recur.
Breadcrumbs
Found during the rewind full-corpus-parse fix (the new _ArtifactSession flows through _resolve_artifacts_corpus, which is where this surfaced). Behavior is identical for SessionInfo and _ArtifactSession, so it predates that change — kept out of that PR to stay focused.
Problem
PrefixId(x)wherexis already aPrefixIdcollapses its.fullto the 8-char short form, becausestr.__new__(PrefixId, a_prefixid)routes throughPrefixId.__str__(which returns.short) rather than copying the full underlying buffer.Repro:
Impact
SessionInfo.session_id(and the new_ArtifactSession.session_id) are alreadyPrefixIds. Several resolvers re-wrap them before comparing:mcp_server.py_resolve_unique_session—PrefixId(s.session_id) == sessionmcp_server.py(~line 891) —PrefixId(s.session_id) == sidmcp_server.py_resolve_artifacts_corpus—PrefixId(s.session_id) == raw_idBecause the wrapped side collapses to 8 chars, equality degrades to an 8-char prefix match. A caller that supplies a >8-char disambiguating prefix to pick one of two sessions sharing the same first 8 chars gets a spurious "ambiguous" error (or, in the unique path, a match that ignored the extra disambiguating chars). Full-id and ≤8-char-prefix lookups are unaffected, so it's latent.
The fix is to drop the redundant re-wrap (
s.session_id == raw_id) sinces.session_idis already aPrefixIdwhose__eq__does prefix matching on the full value. Consider also guardingPrefixId.__new__against re-wrapping aPrefixIdso this can't recur.Breadcrumbs
Found during the rewind full-corpus-parse fix (the new
_ArtifactSessionflows through_resolve_artifacts_corpus, which is where this surfaced). Behavior is identical forSessionInfoand_ArtifactSession, so it predates that change — kept out of that PR to stay focused.