Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions polylogue/storage/sqlite/archive_tiers/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -8350,14 +8350,26 @@ def row_int(key: str) -> int:
session_id = str(row["session_id"])
message_count = int(row["message_count"] or 0)
raw_title = str(row["title"]) if row["title"] is not None else None
provider_title = raw_title if raw_title and raw_title.strip() else None
raw_title_source = str(row["title_source"]) if row["title_source"] is not None else None
# A non-blank ``sessions.title`` is only a genuine provider/derived title
# when ``title_source`` says so. ``title_source='unknown'`` rows still
# carry a NON-NULL title -- the writer's pre-cijx.4 fallback stores the
# raw native id there (a bare UUID, or "<uuid>:agent-<hash>" for a
# subagent), which is exactly the "worse than the UUID it replaces" case
# decision 3 exists to fix. Measured live: 7,501 of 15,401 root sessions
# (48.7%) carry title_source='unknown' -- checking only "is title
# non-blank" (the pre-fix condition) made the structural-label fallback
# dead code for all of them. ``title_source='path'`` is the structural
# label's own prior output; treating it as "not a real title" keeps this
# idempotent on rebuild instead of freezing a stale message count.
has_real_title = bool(raw_title and raw_title.strip()) and raw_title_source in {"origin", "heuristic", "user"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve provider titles without stamped provenance

When a stored title has no title_source, this whitelist now treats it as synthetic. That is normal production data for several origins: polylogue/sources/parsers/chatgpt.py:1101-1106, for example, copies the provider's conversation title into ParsedSession without setting title_source, and the ChatGPT assembly does not add it. Consequently read_summary and list_summaries replace genuine ChatGPT titles with structural labels such as "1 msgs", propagating through CLI/MCP/API summary surfaces. Restrict the fallback to title_source='unknown', or stamp every affected parser before enforcing this whitelist.

Useful? React with 👍 / 👎.

provider_title = raw_title if has_real_title else None
if provider_title is not None:
title = provider_title
title_source = raw_title_source
else:
# No provider-supplied title (or a blank one): fall back to the
# structural label (polylogue-cijx.4 decision 3) rather than
# No provider-supplied title (or a blank/synthetic one): fall back to
# the structural label (polylogue-cijx.4 decision 3) rather than
# exposing a bare/blank title to CLI/MCP/API surfaces. This is a
# read-time projection only -- never written back to sessions.title.
title = session_structural_label_for_session(
Expand Down
59 changes: 59 additions & 0 deletions tests/unit/storage/test_title_source_queryable.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,65 @@ def test_titleless_session_falls_back_to_structural_label(tmp_path: Path) -> Non
assert matched[0].title_source == "path"


def test_unknown_title_source_falls_back_to_structural_label(tmp_path: Path) -> None:
"""polylogue-cijx.4 decision 3: ``title_source='unknown'`` is NOT a real
title, even when ``sessions.title`` is non-blank.

Claude Code's parser (``sources/parsers/claude/code_parser.py``)
initializes ``title`` to the raw composed session id (e.g.
``"<uuid>:agent-<hash>"`` for a subagent) and only promotes
``title_source`` off ``UNKNOWN`` when a real signal (human message,
``agent-name``, ``ai-title``, ``custom-title``) is found. So a real
Claude Code row can carry a non-NULL, non-blank ``title`` *and*
``title_source='unknown'`` simultaneously -- exactly the case decision 3
exists to fix ("a structural label today reads 'agent-<hash> - 27f -
499m' -- worse than the UUID it replaces"). Before this fix,
``_summary_from_row`` treated any non-blank title as a real one
regardless of provenance, so the structural-label fallback never fired
for this population (measured live: 48.7% of root sessions).
"""
from polylogue.storage.sqlite.archive_tiers.archive import ArchiveStore

db_path = tmp_path / "index.db"
with ArchiveStore(tmp_path, initialize=True, read_only=False):
pass

conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
try:
session = ParsedSession(
source_name=Provider.CLAUDE_CODE,
provider_session_id="raw-uuid-1234",
title="raw-uuid-1234", # the code_parser.py raw-id fallback shape
title_source=TitleSource.UNKNOWN,
messages=[
ParsedMessage(
provider_message_id="m1",
role=Role.USER,
text="hi",
position=0,
blocks=[ParsedContentBlock(type=BlockType.TEXT, text="hi")],
),
],
)
write_parsed_session_to_archive(conn, session)
conn.commit()
finally:
conn.close()

with ArchiveStore(tmp_path, initialize=False, read_only=True) as archive:
session_id = archive.resolve_session_id("raw-uuid-1234")
summary = archive.read_summary(session_id)
assert summary.title != "raw-uuid-1234"
assert summary.title_source == "path"

listed = archive.list_summaries(origin="claude-code-session", limit=10, offset=0)
matched = [s for s in listed if s.session_id == session_id]
assert len(matched) == 1
assert matched[0].title != "raw-uuid-1234"
assert matched[0].title_source == "path"


@pytest.mark.asyncio
async def test_session_filter_summary_exposes_title_source(workspace_env: dict[str, Path]) -> None:
"""``SessionFilter.list_summaries()`` yields a domain ``SessionSummary`` with ``title_source`` set."""
Expand Down