import importlib.util from datetime import timezone from pathlib import Path MODULE_PATH = Path(__file__).with_name("collect_issue_digest.py") MODULE_SPEC = importlib.util.spec_from_file_location( "collect_issue_digest", MODULE_PATH ) collect_issue_digest = importlib.util.module_from_spec(MODULE_SPEC) assert MODULE_SPEC.loader is not None MODULE_SPEC.loader.exec_module(collect_issue_digest) def test_build_search_queries_uses_each_owner_and_kind_label(): since = collect_issue_digest.parse_timestamp("2026-04-25T12:34:56Z", "--since") queries = collect_issue_digest.build_search_queries( "openai/codex", ["tui", "exec"], since ) assert queries == [ "repo:openai/codex is:issue updated:>=2026-04-25 label:tui label:bug", "repo:openai/codex is:issue updated:>=2026-04-25 label:tui label:enhancement", "repo:openai/codex is:issue updated:>=2026-04-25 label:exec label:bug", "repo:openai/codex is:issue updated:>=2026-04-25 label:exec label:enhancement", ] def test_build_search_queries_can_scan_all_labels(): since = collect_issue_digest.parse_timestamp("2026-04-25T12:34:56Z", "--since") queries = collect_issue_digest.build_search_queries( "openai/codex", [], since, all_labels=True ) assert queries == [ "repo:openai/codex is:issue updated:>=2026-04-25 label:bug", "repo:openai/codex is:issue updated:>=2026-04-25 label:enhancement", ] def test_normalize_requested_labels_accepts_all_area_phrases(): assert collect_issue_digest.normalize_requested_labels(["all", "areas"]) == ( [], True, ) assert collect_issue_digest.normalize_requested_labels(["all-labels"]) == ( [], True, ) def test_search_issue_numbers_requests_updated_sort(monkeypatch): calls = [] def fake_gh_json(args): calls.append(args) return { "items": [ {"number": 1, "updated_at": "2026-04-25T00:00:00Z"}, ] } monkeypatch.setattr(collect_issue_digest, "gh_json", fake_gh_json) assert collect_issue_digest.search_issue_numbers(["query"], limit=10) == [1] assert "-f" in calls[0] assert "sort=updated" in calls[0] assert "order=desc" in calls[0] def test_search_issue_numbers_applies_limit_per_query(monkeypatch): calls = [] def fake_gh_json(args): calls.append(args) query = next( value.removeprefix("q=") for value in args if value.startswith("q=") ) page = int( next( value.removeprefix("page=") for value in args if value.startswith("page=") ) ) base = 10_000 if query == "first" else 20_000 offset = (page - 1) * 100 return { "items": [ { "number": base + offset + idx, "updated_at": f"2026-04-25T00:{idx:02d}:00Z", } for idx in range(100) ] } monkeypatch.setattr(collect_issue_digest, "gh_json", fake_gh_json) collect_issue_digest.search_issue_numbers(["first", "second"], limit=150) queried_pages = [ ( next( value.removeprefix("q=") for value in args if value.startswith("q=") ), next( value.removeprefix("page=") for value in args if value.startswith("page=") ), ) for args in calls ] assert queried_pages == [ ("first", "1"), ("first", "2"), ("second", "1"), ("second", "2"), ] def test_summarize_issue_keeps_new_comments_and_reaction_signals(): since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") issue = { "number": 123, "title": "TUI does not redraw", "html_url": "https://github.com/openai/codex/issues/123", "state": "open", "created_at": "2026-04-24T20:00:00Z", "updated_at": "2026-04-25T10:00:00Z", "user": {"login": "alice"}, "author_association": "NONE", "comments": 2, "body": "The terminal freezes after resize.", "labels": [{"name": "bug"}, {"name": "tui"}], "reactions": {"total_count": 3, "+1": 2, "rocket": 1}, } comments = [ { "id": 1, "created_at": "2026-04-25T11:00:00Z", "updated_at": "2026-04-25T11:00:00Z", "html_url": "https://github.com/openai/codex/issues/123#issuecomment-1", "user": {"login": "bob"}, "author_association": "MEMBER", "body": "I can reproduce this on main.", "reactions": {"total_count": 4, "heart": 1, "+1": 3}, }, { "id": 2, "created_at": "2026-04-24T11:00:00Z", "updated_at": "2026-04-24T11:00:00Z", "html_url": "https://github.com/openai/codex/issues/123#issuecomment-2", "user": {"login": "carol"}, "author_association": "NONE", "body": "Older comment.", "reactions": {"total_count": 1, "eyes": 1}, }, ] summary = collect_issue_digest.summarize_issue( issue, comments, ["tui", "exec"], since, until, body_chars=200, comment_chars=200, ) assert summary == { "number": 123, "title": "TUI does not redraw", "description": "TUI does not redraw", "url": "https://github.com/openai/codex/issues/123", "state": "open", "author": "alice", "author_association": "NONE", "created_at": "2026-04-24T20:00:00Z", "updated_at": "2026-04-25T10:00:00Z", "labels": ["bug", "tui"], "kind_labels": ["bug"], "owner_labels": ["tui"], "comments_total": 2, "comments_hydration": { "fetched": 2, "since": None, "truncated": False, "max_pages": None, }, "issue_reactions": {"+1": 2, "rocket": 1}, "issue_reaction_total": 3, "comment_reaction_total": 5, "new_comment_reaction_total": 4, "new_issue_reactions": 0, "new_issue_upvotes": 0, "new_comment_reactions": 0, "new_comment_upvotes": 0, "new_reactions": 0, "new_upvotes": 0, "user_interactions": 1, "attention": False, "attention_level": 0, "attention_marker": "", "engagement_score": 12, "activity": { "new_issue": False, "new_comments": 1, "new_human_comments": 1, "new_reactions": 0, "new_upvotes": 0, "updated_without_visible_new_post": False, }, "body_excerpt": "The terminal freezes after resize.", "new_comments": [ { "id": 1, "author": "bob", "author_association": "MEMBER", "created_at": "2026-04-25T11:00:00Z", "updated_at": "2026-04-25T11:00:00Z", "url": "https://github.com/openai/codex/issues/123#issuecomment-1", "human_user_interaction": True, "reactions": {"+1": 3, "heart": 1}, "reaction_total": 4, "new_reactions": 0, "new_upvotes": 0, "new_reaction_counts": {}, "body_excerpt": "I can reproduce this on main.", } ], } def test_summarize_issue_filters_non_owner_or_non_kind_labels(): since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") base_issue = { "number": 1, "title": "Question", "created_at": "2026-04-25T01:00:00Z", "updated_at": "2026-04-25T01:00:00Z", "labels": [{"name": "question"}, {"name": "tui"}], } assert ( collect_issue_digest.summarize_issue( base_issue, [], ["tui"], since, until, body_chars=100, comment_chars=100, ) is None ) issue_without_owner = dict(base_issue) issue_without_owner["labels"] = [{"name": "bug"}, {"name": "app"}] assert ( collect_issue_digest.summarize_issue( issue_without_owner, [], ["tui"], since, until, body_chars=100, comment_chars=100, ) is None ) def test_resolve_window_defaults_to_previous_hours(): class Args: since = None until = "2026-04-26T12:00:00Z" window_hours = 24 since, until = collect_issue_digest.resolve_window(Args()) assert since.isoformat() == "2026-04-25T12:00:00+00:00" assert until.tzinfo == timezone.utc def test_parse_duration_hours_accepts_common_phrases(): assert collect_issue_digest.parse_duration_hours("past week") == 168 assert collect_issue_digest.parse_duration_hours("48h") == 48 assert collect_issue_digest.parse_duration_hours("2 days") == 48 assert collect_issue_digest.parse_duration_hours("1w") == 168 def test_attention_thresholds_scale_by_window_length(): one_day = collect_issue_digest.attention_thresholds_for_window(24) assert one_day["elevated"] == 5 assert one_day["very_high"] == 10 half_day = collect_issue_digest.attention_thresholds_for_window(12) assert half_day["elevated"] == 3 assert half_day["very_high"] == 5 week = collect_issue_digest.attention_thresholds_for_window(168) assert week["elevated"] == 35 assert week["very_high"] == 70 assert collect_issue_digest.attention_marker_for(34, week) == "" assert collect_issue_digest.attention_marker_for(35, week) == "🔥" assert collect_issue_digest.attention_marker_for(70, week) == "🔥🔥" def test_fetch_comments_uses_since_filter_and_page_cap(monkeypatch): calls = [] def fake_gh_json(args): calls.append(args) return [{"id": idx} for idx in range(100)] monkeypatch.setattr(collect_issue_digest, "gh_json", fake_gh_json) since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") payload = collect_issue_digest.fetch_comments( "openai/codex", 123, since=since, max_pages=1 ) assert len(payload["items"]) == 100 assert payload["truncated"] is True assert payload["max_pages"] == 1 assert calls == [ [ "api", "repos/openai/codex/issues/123/comments?since=2026-04-25T00%3A00%3A00Z&per_page=100&page=1", ] ] def test_issue_description_prefers_title_over_body_noise(): issue = { "title": "Codex.app GUI: MCP child processes not reaped after task completion", "body": "A later crash mention should not override the title-level symptom.", "labels": [{"name": "app"}, {"name": "bug"}], } description = collect_issue_digest.issue_description(issue) assert "MCP child processes" in description assert "crash" not in description.casefold() def test_attention_markers_count_human_user_interactions(): since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") issue = { "number": 456, "title": "Agent context is exploding", "html_url": "https://github.com/openai/codex/issues/456", "state": "open", "created_at": "2026-04-25T01:00:00Z", "updated_at": "2026-04-25T12:00:00Z", "user": {"login": "alice"}, "labels": [{"name": "bug"}, {"name": "agent"}], } comments = [ { "id": idx, "created_at": "2026-04-25T02:00:00Z", "updated_at": "2026-04-25T02:00:00Z", "user": {"login": f"user-{idx}"}, "body": "same here", } for idx in range(4) ] comments.append( { "id": 99, "created_at": "2026-04-25T02:00:00Z", "updated_at": "2026-04-25T02:00:00Z", "user": {"login": "github-actions[bot]"}, "body": "duplicate bot note", } ) summary = collect_issue_digest.summarize_issue( issue, comments, ["agent"], since, until, body_chars=100, comment_chars=100, ) assert summary["user_interactions"] == 5 assert summary["activity"]["new_human_comments"] == 4 assert summary["attention"] is True assert summary["attention_level"] == 1 assert summary["attention_marker"] == "🔥" issue["created_at"] = "2026-04-24T01:00:00Z" comments.extend( { "id": idx, "created_at": "2026-04-25T03:00:00Z", "updated_at": "2026-04-25T03:00:00Z", "user": {"login": f"extra-user-{idx}"}, "body": "also seeing this", } for idx in range(100, 106) ) summary = collect_issue_digest.summarize_issue( issue, comments, ["agent"], since, until, body_chars=100, comment_chars=100, ) assert summary["user_interactions"] == 10 assert summary["attention_level"] == 2 assert summary["attention_marker"] == "🔥🔥" def test_reactions_count_toward_attention_markers(): since = collect_issue_digest.parse_timestamp("2026-04-25T00:00:00Z", "--since") until = collect_issue_digest.parse_timestamp("2026-04-26T00:00:00Z", "--until") issue = { "number": 789, "title": "Support 1M token context", "html_url": "https://github.com/openai/codex/issues/789", "state": "open", "created_at": "2026-04-24T01:00:00Z", "updated_at": "2026-04-25T12:00:00Z", "user": {"login": "alice"}, "labels": [{"name": "enhancement"}, {"name": "context"}], "reactions": {"total_count": 20, "+1": 20}, } comments = [ { "id": 1, "created_at": "2026-04-25T02:00:00Z", "updated_at": "2026-04-25T02:00:00Z", "user": {"login": "commenter"}, "body": "please", "reactions": {"total_count": 2, "+1": 2}, } ] issue_reactions = [ { "content": "+1", "created_at": "2026-04-25T03:00:00Z", "user": {"login": f"reactor-{idx}"}, } for idx in range(18) ] comment_reactions_by_id = { 1: [ { "content": "heart", "created_at": "2026-04-25T04:00:00Z", "user": {"login": "human-reactor"}, }, { "content": "+1", "created_at": "2026-04-25T04:00:00Z", "user": {"login": "github-actions[bot]"}, }, ] } summary = collect_issue_digest.summarize_issue( issue, comments, ["context"], since, until, body_chars=100, comment_chars=100, issue_reaction_events=issue_reactions, comment_reactions_by_id=comment_reactions_by_id, ) assert summary["new_reactions"] == 19 assert summary["new_upvotes"] == 18 assert summary["user_interactions"] == 20 assert summary["attention_level"] == 2 assert summary["attention_marker"] == "🔥🔥" assert summary["new_comments"][0]["new_reactions"] == 1 assert summary["new_comments"][0]["new_upvotes"] == 0 def test_digest_rows_are_table_ready_with_concise_descriptions(): rows = collect_issue_digest.digest_rows( [ { "number": 1, "title": "Quiet bug", "description": "Quiet bug", "url": "https://github.com/openai/codex/issues/1", "owner_labels": ["context"], "kind_labels": ["bug"], "state": "open", "attention": False, "attention_level": 0, "attention_marker": "", "user_interactions": 1, "new_reactions": 0, "new_upvotes": 0, "engagement_score": 3, "issue_reaction_total": 0, "comment_reaction_total": 0, "updated_at": "2026-04-25T01:00:00Z", "activity": { "new_issue": True, "new_comments": 0, "new_reactions": 0, "updated_without_visible_new_post": False, }, }, { "number": 2, "title": "Busy bug", "description": "High-volume bug report", "url": "https://github.com/openai/codex/issues/2", "owner_labels": ["agent"], "kind_labels": ["bug"], "state": "open", "attention": True, "attention_level": 1, "attention_marker": "🔥", "user_interactions": 17, "new_reactions": 3, "new_upvotes": 2, "engagement_score": 20, "issue_reaction_total": 5, "comment_reaction_total": 2, "updated_at": "2026-04-25T02:00:00Z", "activity": { "new_issue": False, "new_comments": 16, "new_reactions": 3, "updated_without_visible_new_post": False, }, }, ] ) assert rows[0] == { "ref": 1, "ref_markdown": "[1](https://github.com/openai/codex/issues/2)", "marker": "🔥", "attention_marker": "🔥", "number": 2, "description": "High-volume bug report", "title": "Busy bug", "url": "https://github.com/openai/codex/issues/2", "area": "agent", "kind": "bug", "state": "open", "interactions": 17, "user_interactions": 17, "new_reactions": 3, "new_upvotes": 2, "current_reactions": 7, } def test_summary_inputs_are_model_ready_without_preclustering(): issues = [ { "number": 20, "title": "Windows app Browser Use external navigation fails", "description": "Browser Use navigation or app-server failure", "url": "https://github.com/openai/codex/issues/20", "labels": ["app", "bug"], "owner_labels": ["app"], "kind_labels": ["bug"], "attention": False, "attention_level": 0, "attention_marker": "", "user_interactions": 3, "new_reactions": 1, "engagement_score": 8, "updated_at": "2026-04-25T04:00:00Z", "activity": {"new_comments": 2}, }, { "number": 21, "title": "On Windows, cmake output waits until timeout", "description": "Windows command timeout/capture problem", "url": "https://github.com/openai/codex/issues/21", "labels": ["app", "bug"], "owner_labels": ["app"], "kind_labels": ["bug"], "attention": False, "attention_level": 0, "attention_marker": "", "user_interactions": 3, "new_reactions": 0, "engagement_score": 7, "updated_at": "2026-04-25T03:00:00Z", "activity": {"new_comments": 3}, }, { "number": 22, "title": "Windows computer use tool fails to click buttons", "description": "Computer-use workflow failure", "url": "https://github.com/openai/codex/issues/22", "labels": ["app", "bug"], "owner_labels": ["app"], "kind_labels": ["bug"], "attention": False, "attention_level": 0, "attention_marker": "", "user_interactions": 3, "new_reactions": 0, "engagement_score": 6, "updated_at": "2026-04-25T02:00:00Z", "activity": {"new_comments": 3}, }, ] rows = collect_issue_digest.summary_inputs(issues, ref_map={20: 1, 21: 2, 22: 3}) assert rows == [ { "ref": 1, "ref_markdown": "[1](https://github.com/openai/codex/issues/20)", "number": 20, "title": "Windows app Browser Use external navigation fails", "description": "Browser Use navigation or app-server failure", "url": "https://github.com/openai/codex/issues/20", "labels": ["app", "bug"], "owner_labels": ["app"], "kind_labels": ["bug"], "state": "", "attention_marker": "", "interactions": 3, "new_comments": 2, "new_reactions": 1, "new_upvotes": 0, "current_reactions": 0, }, { "ref": 2, "ref_markdown": "[2](https://github.com/openai/codex/issues/21)", "number": 21, "title": "On Windows, cmake output waits until timeout", "description": "Windows command timeout/capture problem", "url": "https://github.com/openai/codex/issues/21", "labels": ["app", "bug"], "owner_labels": ["app"], "kind_labels": ["bug"], "state": "", "attention_marker": "", "interactions": 3, "new_comments": 3, "new_reactions": 0, "new_upvotes": 0, "current_reactions": 0, }, { "ref": 3, "ref_markdown": "[3](https://github.com/openai/codex/issues/22)", "number": 22, "title": "Windows computer use tool fails to click buttons", "description": "Computer-use workflow failure", "url": "https://github.com/openai/codex/issues/22", "labels": ["app", "bug"], "owner_labels": ["app"], "kind_labels": ["bug"], "state": "", "attention_marker": "", "interactions": 3, "new_comments": 3, "new_reactions": 0, "new_upvotes": 0, "current_reactions": 0, }, ]