From e71ccd309c109c7d9d6aeccbbd3e193993af240d Mon Sep 17 00:00:00 2001 From: Daniel Demmel Date: Tue, 7 Jul 2026 00:02:23 +0000 Subject: [PATCH] Fix ty possibly-missing-attribute warnings in tests ctx.messages is list[Optional[TemplateMessage]] (ghost slots are None), so comprehensions iterating it directly need an 'm is not None' guard. In test_skill_pairing, collect the narrowed SessionHeaderMessage contents instead of the wrapper messages so the isinstance narrowing survives into the follow-up comprehensions. Co-Authored-By: Claude Fable 5 --- test/test_dag_integration.py | 14 ++++++++++---- test/test_hook_attachment_rendering.py | 18 ++++++++++++++---- test/test_skill_pairing.py | 8 ++++---- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/test/test_dag_integration.py b/test/test_dag_integration.py index 35015062..a61bc69c 100644 --- a/test/test_dag_integration.py +++ b/test/test_dag_integration.py @@ -856,7 +856,7 @@ def test_agent_session_has_no_separate_header(self, tmp_path: Path) -> None: header_session_ids = { m.content.session_id for m in context.messages - if isinstance(m.content, SessionHeaderMessage) + if m is not None and isinstance(m.content, SessionHeaderMessage) } assert header_session_ids == {"s1"} @@ -1025,7 +1025,9 @@ def test_agent_in_branch(self, tmp_path: Path) -> None: # Verify message ordering: agent messages should be in the branch # block (after b1_u anchor, before branch 2's b2_u) - msg_uuids = {m.meta.uuid: m.message_index for m in context.messages} + msg_uuids = { + m.meta.uuid: m.message_index for m in context.messages if m is not None + } assert "ag1" in msg_uuids assert "b1_u" in msg_uuids assert "b2_u" in msg_uuids @@ -1154,7 +1156,9 @@ def test_second_session_not_polluted_by_first_session_branch( _, _, ctx = generate_template_messages(messages, session_tree=session_tree) # Find messages from session s2 by UUID - s2_msgs = [m for m in ctx.messages if m.meta.uuid in ("u3", "a3")] + s2_msgs = [ + m for m in ctx.messages if m is not None and m.meta.uuid in ("u3", "a3") + ] assert len(s2_msgs) == 2, f"Expected 2 s2 messages, got {len(s2_msgs)}" for msg in s2_msgs: @@ -1579,7 +1583,9 @@ def test_attachment_not_rendered(self, tmp_path: Path) -> None: _, _, ctx = generate_template_messages(messages, session_tree=session_tree) # No message should have uuid "att1" - rendered_uuids = [m.meta.uuid for m in ctx.messages if m.meta.uuid] + rendered_uuids = [ + m.meta.uuid for m in ctx.messages if m is not None and m.meta.uuid + ] assert "att1" not in rendered_uuids # But user and assistant should be rendered assert "u1" in rendered_uuids diff --git a/test/test_hook_attachment_rendering.py b/test/test_hook_attachment_rendering.py index 9a5aaed2..6834ef95 100644 --- a/test/test_hook_attachment_rendering.py +++ b/test/test_hook_attachment_rendering.py @@ -397,7 +397,9 @@ def test_full_detail_renders_hook_attachment(self) -> None: del roots, _nav # only inspect ctx.messages attachments = [ - m for m in ctx.messages if isinstance(m.content, HookAttachmentMessage) + m + for m in ctx.messages + if m is not None and isinstance(m.content, HookAttachmentMessage) ] assert len(attachments) == 1 msg = attachments[0] @@ -507,7 +509,9 @@ def test_hook_does_not_pair_with_chained_system_entry(self) -> None: del _roots, _nav hook = next( - m for m in ctx.messages if isinstance(m.content, HookAttachmentMessage) + m + for m in ctx.messages + if m is not None and isinstance(m.content, HookAttachmentMessage) ) # No pair links on either side — the hook stands alone. assert hook.pair_first is None @@ -549,14 +553,20 @@ def test_hook_does_not_claim_system_info_as_child(self) -> None: del _roots, _nav hook = next( - m for m in ctx.messages if isinstance(m.content, HookAttachmentMessage) + m + for m in ctx.messages + if m is not None and isinstance(m.content, HookAttachmentMessage) ) # Filter to user-content SystemMessages (the "/color" + "Session # color set" entries) — exclude SessionHeaderMessage which also # carries msg_type "system". from claude_code_log.models import SystemMessage - system_infos = [m for m in ctx.messages if isinstance(m.content, SystemMessage)] + system_infos = [ + m + for m in ctx.messages + if m is not None and isinstance(m.content, SystemMessage) + ] # Hook should NOT have any system_info as immediate child. assert hook.immediate_children_count == 0, ( diff --git a/test/test_skill_pairing.py b/test/test_skill_pairing.py index d237ed74..5cfa9bd8 100644 --- a/test/test_skill_pairing.py +++ b/test/test_skill_pairing.py @@ -759,21 +759,21 @@ def test_skill_fold_inside_fork_at_full_keeps_branch_backref( # long as the fork anchor itself wasn't ghosted (it wasn't — # the trunk-side anchor is never a Skill-fold target). branch_headers = [ - m + m.content for m in survivors if isinstance(m.content, SessionHeaderMessage) and m.content.is_branch ] # The Skill-bearing branch is the one rooted at 'a-skill'. # (Branch sids are ``{trunk}@{first_uuid12}`` per dag.py.) skill_branches = [ - m for m in branch_headers if m.content.session_id.endswith("@a-skill") + h for h in branch_headers if h.session_id.endswith("@a-skill") ] assert len(skill_branches) == 1, ( f"expected exactly one branch header rooted at 'a-skill'; got " - f"{[m.content.session_id for m in branch_headers]}" + f"{[h.session_id for h in branch_headers]}" ) skill_branch = skill_branches[0] - parent_idx = skill_branch.content.parent_message_index + parent_idx = skill_branch.parent_message_index assert parent_idx is not None, ( "skill-bearing branch header's parent_message_index is None — " "expected it to resolve to the fork anchor 'c'."