Unofficial Python API and agentic skill for Google NotebookLM. Full programmatic access to NotebookLM's features—including capabilities the web UI doesn't expose—via Python, CLI, and AI agents like Claude Code, Codex, and OpenClaw.
This report presents the forensic synthetic code analysis of teng-lin/notebooklm-py, a Python project with 17,744 GitHub stars. SynthScan v2.0 examined 574,232 lines of code across 1156 source files, recording 13845 pattern matches distributed across 21 syntactic categories. The overall adjusted score of 35.6 places this repository in the Strong AI signal band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
This chart maps the temporal evolution of the adjusted synthetic code score across successive scan runs. An upward trajectory indicates ongoing incorporation of AI-generated code or expanding LLM-assisted scaffolding; a stable or declining trajectory may reflect active human refactoring, code removal, or the adoption of stricter authorship policies. The dashed secondary line (right axis) independently tracks total raw pattern hit count, which can diverge from the normalised score when codebase size changes significantly between scans.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 13845 distinct pattern matches across 21 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/cassette_patterns.py | 715 | # ============================================================================= | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 717 | # ============================================================================= | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 724 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 726 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 751 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 753 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 760 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 762 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 770 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 772 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 779 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 781 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 856 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 858 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 868 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 870 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 888 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 890 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 895 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 897 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 1193 | # ============================================================================= | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 1195 | # ============================================================================= | COMMENT |
| MEDIUM⚡ | tests/cassette_patterns.py | 1198 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 132 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 134 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 250 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 252 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 303 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 305 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 319 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 321 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 363 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 365 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 405 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 407 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 512 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 514 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 795 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 797 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 822 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 824 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 837 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 839 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 910 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 912 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 934 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 936 | # ------------------------------------------------------------------------- | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 949 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 951 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 1499 | # ============================================================================= | COMMENT |
| MEDIUM | tests/cassette_patterns.py | 1501 | # ============================================================================= | COMMENT |
| MEDIUM | tests/vcr_config.py | 319 | # ============================================================================= | COMMENT |
| MEDIUM | tests/vcr_config.py | 321 | # ============================================================================= | COMMENT |
| MEDIUM | tests/vcr_config.py | 761 | # ============================================================================= | COMMENT |
| MEDIUM | tests/vcr_config.py | 763 | # ============================================================================= | COMMENT |
| MEDIUM⚡ | tests/unit/test_artifacts_helpers.py | 25 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/unit/test_artifacts_helpers.py | 27 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/unit/test_artifacts_helpers.py | 46 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/unit/test_artifacts_helpers.py | 48 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM⚡ | tests/unit/test_artifacts_helpers.py | 78 | # --------------------------------------------------------------------------- | COMMENT |
| 2344 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/conftest.py | 285 | def pytest_collection_modifyitems(config, items): | CODE |
| LOW | tests/conftest.py | 342 | def mock_list_notebooks_response(): | STRING |
| LOW | tests/cassette_patterns.py | 1532 | def synthetic_error_cassette_name(mode: str, slug: str) -> str: | CODE |
| LOW | tests/cassette_patterns.py | 1549 | def build_synthetic_error_response( | CODE |
| LOW | tests/vcr_config.py | 181 | def _substitute_synthetic_error(response: dict[str, Any]) -> dict[str, Any]: | CODE |
| LOW | tests/vcr_config.py | 502 | def _is_create_artifact_client_options(node: Any) -> bool: | CODE |
| LOW | tests/vcr_config.py | 518 | def _normalize_create_artifact_options(decoded_outer: Any) -> Any: | CODE |
| LOW | tests/_helpers/client_factory.py | 29 | def build_client_shell_for_tests( | CODE |
| LOW⚡ | tests/unit/test_artifacts_helpers.py | 30 | def test_extract_data_table_rows_happy_path() -> None: | CODE |
| LOW⚡ | tests/unit/test_artifacts_helpers.py | 51 | def test_extract_data_table_rows_missing_inner_list() -> None: | CODE |
| LOW⚡ | tests/unit/test_artifacts_helpers.py | 61 | def test_extract_data_table_rows_wrong_type_at_one_level() -> None: | CODE |
| LOW⚡ | tests/unit/test_artifacts_helpers.py | 70 | def test_extract_data_table_rows_truncated_structure() -> None: | CODE |
| LOW⚡ | tests/unit/test_artifacts_helpers.py | 83 | def test_extract_data_table_rows_non_list_inner_value( | CODE |
| LOW⚡ | tests/unit/test_artifacts_helpers.py | 105 | def test_parse_data_table_raises_artifact_parse_error_on_drift() -> None: | CODE |
| LOW | tests/unit/test_artifacts_helpers.py | 118 | def test_parse_data_table_raises_on_empty_rows() -> None: | CODE |
| LOW | tests/unit/test_artifacts_helpers.py | 126 | def test_parse_data_table_happy_path() -> None: | CODE |
| LOW | tests/unit/test_artifacts_helpers.py | 154 | def test_parse_data_table_skips_malformed_row_sections() -> None: | CODE |
| LOW⚡ | tests/unit/test_json_stdout_purity.py | 1140 | def test_matched_partial_id_goes_to_stderr_in_json_mode(runner: CliRunner, mock_auth_env) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 215 | async def wait_for_research_completion( | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 302 | def _customize_suggest_prompts(client: MagicMock) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 315 | def _customize_share_view_level(client: MagicMock) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 319 | def _customize_source_fulltext(client: MagicMock) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 341 | def _customize_source_add_research(client: MagicMock) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 368 | def _customize_research_cancel(client: MagicMock) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 374 | def _customize_notebook_create(client: MagicMock) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 645 | def test_json_mode_stdout_is_parseable( | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 1040 | def test_all_json_commands_have_sweep_entry() -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 1159 | def test_matched_partial_id_still_goes_to_stdout_in_human_mode( | CODE |
| LOW | tests/unit/test_client_keepalive.py | 61 | async def _read_storage_text_when_available(storage_path): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 71 | async def _wait_until_storage_contains(storage_path, needle: str, failure_message: str) -> None: | CODE |
| LOW | tests/unit/test_client_keepalive.py | 83 | async def _wait_for_rotate_requests( | CODE |
| LOW | tests/unit/test_client_keepalive.py | 104 | async def test_keepalive_off_by_default(self, mock_auth, httpx_mock: HTTPXMock): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 120 | async def test_spawns_task_on_enter_cancels_on_exit(self, mock_auth, httpx_mock: HTTPXMock): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 148 | async def test_floor_clamps_low_interval(self, mock_auth): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 158 | async def test_floor_does_not_lower_higher_interval(self, mock_auth): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 180 | def test_rejects_non_positive_or_non_finite_keepalive(self, mock_auth, bad): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 186 | def test_rejects_non_positive_or_non_finite_floor(self, mock_auth, bad): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 227 | async def test_failure_does_not_crash_loop(self, mock_auth, httpx_mock: HTTPXMock, monkeypatch): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 273 | async def test_persistence_failure_logs_warning_and_continues( | CODE |
| LOW | tests/unit/test_client_keepalive.py | 321 | async def test_explicit_storage_path_used_when_auth_lacks_one( | CODE |
| LOW | tests/unit/test_client_keepalive.py | 374 | def test_explicit_storage_path_normalizes_onto_auth_without_mutating_caller(self, tmp_path): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 406 | async def test_close_persists_to_explicit_storage_path(self, tmp_path, httpx_mock: HTTPXMock): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 442 | async def test_persists_rotated_cookies_without_aexit(self, tmp_path, httpx_mock: HTTPXMock): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 485 | async def test_save_cookies_takes_in_process_lock_before_writing(self, tmp_path): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 536 | async def test_refresh_auth_routes_save_through_save_cookies( | CODE |
| LOW | tests/unit/test_client_keepalive.py | 620 | def test_save_cookies_to_storage_acquires_file_lock(self, tmp_path, monkeypatch): | CODE |
| LOW | tests/unit/test_client_keepalive.py | 662 | def test_save_cookies_to_storage_creates_lock_sentinel(self, tmp_path): | CODE |
| LOW | tests/unit/test_drain_tracker_hooks.py | 26 | async def test_run_drain_hooks_fires_in_registration_order() -> None: | CODE |
| LOW | tests/unit/test_drain_tracker_hooks.py | 54 | async def test_run_drain_hooks_continues_when_one_hook_raises(caplog) -> None: | CODE |
| LOW | tests/unit/test_drain_tracker_hooks.py | 93 | async def test_run_drain_hooks_is_noop_when_none_registered() -> None: | CODE |
| LOW | tests/unit/test_drain_tracker_hooks.py | 101 | async def test_register_drain_hook_overwrites_same_name() -> None: | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1626 | def test_parse_single_citation_returns_none_when_not_list(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1632 | def test_parse_single_citation_returns_none_when_too_short(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1638 | def test_parse_single_citation_returns_none_when_cite_inner_not_list(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1644 | def test_parse_single_citation_returns_none_when_no_source_id(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1732 | def test_non_list_input_returns_immediately(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1739 | def test_non_list_input_none_returns_immediately(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1746 | def test_nested_group_not_list_is_skipped(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1754 | def test_text_val_is_list_extracts_strings(self, auth_tokens): | CODE |
| LOW⚡ | tests/unit/test_chat_characterization.py | 1764 | def test_text_val_list_skips_non_strings(self, auth_tokens): | CODE |
| 8530 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | hatch_build.py | 21 | CODE | |
| LOW | desktop-extension/run_server.py | 24 | CODE | |
| LOW | tests/cassette_patterns.py | 126 | CODE | |
| LOW | tests/_helpers/client_factory.py | 3 | CODE | |
| LOW | tests/unit/test_artifacts_helpers.py | 12 | CODE | |
| LOW | tests/unit/test_json_stdout_purity.py | 19 | CODE | |
| LOW | tests/unit/test_drain_tracker_hooks.py | 18 | CODE | |
| LOW | tests/unit/test_refresh_lock_registry.py | 11 | CODE | |
| LOW | tests/unit/test_from_storage_dual_protocol.py | 18 | CODE | |
| LOW | tests/unit/test_curl_cffi_transport_poc.py | 14 | CODE | |
| LOW | tests/unit/test_firefox_containers.py | 12 | CODE | |
| LOW | tests/unit/test_vcr_scrubbing.py | 30 | CODE | |
| LOW | tests/unit/test_auth_instance_invariant.py | 3 | CODE | |
| LOW | tests/unit/test_auth_session_master_token.py | 3 | CODE | |
| LOW | tests/unit/test_vcr_config.py | 20 | CODE | |
| LOW | tests/unit/test_source_upload_coverage.py | 10 | CODE | |
| LOW | tests/unit/test_auth_master_token.py | 7 | CODE | |
| LOW | tests/unit/test_tier_enforcement_hook.py | 39 | CODE | |
| LOW | tests/unit/test_auth_refresh_retry.py | 12 | CODE | |
| LOW | tests/unit/test_serving.py | 9 | CODE | |
| LOW | tests/unit/test_streaming_chat_wire.py | 3 | CODE | |
| LOW | tests/unit/test_streaming_chat_wire.py | 797 | CODE | |
| LOW | tests/unit/test_get_or_none.py | 15 | CODE | |
| LOW | tests/unit/test_browser_capture_cdp_arm.py | 25 | CODE | |
| LOW | tests/unit/test_concurrency_refresh_race.py | 58 | CODE | |
| LOW | tests/unit/test_middleware_chain_host.py | 28 | CODE | |
| LOW | tests/unit/test_capture_rpc_registry.py | 8 | CODE | |
| LOW | tests/unit/test_generation_state.py | 15 | CODE | |
| LOW | tests/unit/test_research_row_adapter.py | 15 | CODE | |
| LOW | tests/unit/test_with_client_handle_errors.py | 20 | CODE | |
| LOW | tests/unit/test_logging.py | 3 | CODE | |
| LOW | tests/unit/test_label_exceptions.py | 3 | CODE | |
| LOW | tests/unit/test_check_rpc_health.py | 19 | CODE | |
| LOW | tests/unit/test_auth_headless_reauth.py | 16 | CODE | |
| LOW | tests/unit/test_public_api_behavior.py | 40 | CODE | |
| LOW | tests/unit/test_refresh_state_machine.py | 14 | CODE | |
| LOW | tests/unit/test_download_redirect_revalidation.py | 25 | CODE | |
| LOW | tests/unit/test_logging_correlation.py | 3 | CODE | |
| LOW | tests/unit/test_download_result.py | 3 | CODE | |
| LOW | tests/unit/test_chat_passage_resolver.py | 15 | CODE | |
| LOW | tests/unit/test_auth_session_headless_reauth.py | 19 | CODE | |
| LOW | tests/unit/test_chat_helpers.py | 11 | CODE | |
| LOW | tests/unit/test_auth_psidts_recovery.py | 12 | CODE | |
| LOW | tests/unit/test_mind_maps_api.py | 3 | CODE | |
| LOW | tests/unit/test_error_injection_middleware.py | 41 | CODE | |
| LOW | tests/unit/test_drive_import.py | 15 | CODE | |
| LOW | tests/unit/test_chain_wiring.py | 27 | CODE | |
| LOW | tests/unit/test_interactive_mind_map.py | 11 | CODE | |
| LOW | tests/unit/test_playwright_marker_hook.py | 22 | CODE | |
| LOW | tests/unit/test_e2e_conftest_options.py | 11 | CODE | |
| LOW | tests/unit/test_auth_account_coverage.py | 13 | CODE | |
| LOW | tests/unit/test_runtime_auth.py | 35 | CODE | |
| LOW | tests/unit/test_cookie_domain_split.py | 21 | CODE | |
| LOW | tests/unit/test_notebook_metadata.py | 3 | CODE | |
| LOW | tests/unit/test_download_url.py | 11 | CODE | |
| LOW | tests/unit/test_label_model.py | 3 | CODE | |
| LOW | tests/unit/test_tier_13_all_exports.py | 15 | CODE | |
| LOW | tests/unit/test_profile_atomic_write.py | 38 | CODE | |
| LOW | tests/unit/test_sources_row_adapter.py | 22 | CODE | |
| LOW | tests/unit/test_atomic_io.py | 14 | CODE | |
| 1493 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/vcr_config.py | 572 | 1. **Streaming-chat envelope** ``[null, "<inner_json>"]`` where the inner | STRING |
| HIGH | tests/vcr_config.py | 578 | 2. **Batchexecute envelope** ``[[[rpc_id, args_json, null, "generic"]]]`` | STRING |
| HIGH | tests/vcr_config.py | 647 | # Streaming-chat envelope: [null, "<inner_json>"]. | COMMENT |
| HIGH | tests/unit/test_vcr_config.py | 84 | JSON envelope is ``[null, "<inner_json>"]`` and ``<inner_json>`` is itself a | STRING |
| HIGH⚡ | tests/unit/test_check_rpc_health.py | 798 | body = ')]}\'\n\n[["wrb.fr","sqTeoe","null",null,null,null,"generic"]]' | CODE |
| HIGH⚡ | tests/unit/test_check_rpc_health.py | 812 | body = ')]}\'\n\n[["wrb.fr","sqTeoe","[[1,2,3]]",null,null,null,"generic"]]' | CODE |
| HIGH⚡ | tests/unit/test_decoder.py | 866 | """[5, null, 'x'] is not the bare form — no enrichment.""" | STRING |
| HIGH | tests/unit/test_chat_ask_invariants.py | 377 | '63\n[["wrb.fr","hPTbtc","[[[\\"real-conv-id-from-hptbtc\\"]]]",null,null]]' | CODE |
| HIGH | tests/unit/test_api_coverage.py | 149 | # Real API returns 3 levels of nesting: [[[null, [summary], [[keywords]], []]]] | COMMENT |
| HIGH | tests/unit/test_api_coverage.py | 186 | # Response format: [[[title, description, null, null, prompt, audience_level], ...]] | COMMENT |
| HIGH⚡ | tests/unit/test_api_coverage.py | 301 | # Verify reference payload: [null, ["source_id"], [2]] | COMMENT |
| HIGH⚡ | tests/unit/test_api_coverage.py | 317 | # Verify reference payload: [null, ["source_id"], [2]] | COMMENT |
| HIGH | tests/unit/test_authed_post_pipeline.py | 1598 | decoded_payload = b')]}\'\n\n[["wrb.fr",null,"[1]",null,null,null,"generic"]]' | CODE |
| HIGH | tests/unit/test_select_artifact.py | 276 | ever emits ``[null, ...]`` at index 15 the sort must coerce that | STRING |
| HIGH | tests/unit/test_auth_cookie_save_race.py | 1017 | f"None-valued cookie must never be persisted as value:null, got: {stored}" | CODE |
| HIGH | tests/unit/test_save_chat_as_note_encoder.py | 106 | # passage[3] = [[null, start, end]]; passage[5] = [[[passage_id], source_id]]; | COMMENT |
| HIGH | tests/unit/test_save_chat_as_note_encoder.py | 202 | assert "[0,0,0,null,null,null,null,0,0]" in actual_json | CODE |
| HIGH⚡ | tests/unit/test_user_settings_api.py | 145 | # GET_USER_SETTINGS inner: [[null,[..limits..],[true,null,null,true,["fr"]], | COMMENT |
| HIGH⚡ | tests/unit/test_user_settings_api.py | 147 | # SET_USER_SETTINGS inner: [null,[..limits..],[true,null,null,true,["en"]], | COMMENT |
| HIGH | tests/unit/test_rate_limit_retry.py | 47 | resp.text = ")]}'\n[null,[" + str(payload).replace("'", '"') + "]]" | CODE |
| HIGH | tests/unit/test_rescrub_cassettes_script.py | 89 | f'[["wrb.fr","JFMDGd","[[null,[\\"{avatar_url}\\"]]]",null,null,null,"generic"]]' | CODE |
| HIGH | tests/unit/test_scrub_rpc_har.py | 59 | freq = json.dumps([[["CCqFvf", '["t",null,null,[2],[1]]', None, "generic"]]]) | CODE |
| HIGH | tests/unit/test_scrub_rpc_har.py | 146 | freq = json.dumps([[["CCqFvf", '["t",null,null,[2],[1]]', None, "generic"]]]) | CODE |
| HIGH⚡ | tests/unit/test_rpc_overrides.py | 173 | '{"LIST_NOTEBOOKS": null, "CREATE_NOTEBOOK": "valid"}', | CODE |
| HIGH⚡ | tests/unit/test_vcr_body_matcher.py | 117 | """Build a streaming-chat ``f.req`` form body (`[null, "<inner_json>"]`).""" | STRING |
| HIGH⚡ | tests/unit/test_vcr_body_matcher.py | 208 | fixtures: the cassette has a trailing ``[null, null]`` while the live | STRING |
| HIGH | tests/unit/test_vcr_body_matcher.py | 106 | is ``[[[rpc_id, "<args_json>", null, "generic"]]]`` and ``<args_json>`` | STRING |
| HIGH | tests/unit/test_vcr_body_matcher.py | 394 | The outer ``[[[rpc, "...", null, "generic"]]]`` shape parses cleanly, | STRING |
| HIGH⚡ | tests/unit/test_chat_row_adapter.py | 332 | # ``[None]`` (leading slot present but null) yields ``None`` — the | COMMENT |
| HIGH | tests/unit/test_source_selection.py | 1061 | # Response format: [[[title, description, null, null, prompt, audience_level], ...]] | COMMENT |
| HIGH⚡ | tests/unit/mcp/test_sources.py | 233 | # ``status: Literal[...] | None`` serializes as an ``anyOf`` of {enum} + {null}. | COMMENT |
| HIGH⚡ | tests/unit/mcp/test_sources.py | 1541 | {"source": null} as success — it projects NOT_FOUND.""" | STRING |
| HIGH | tests/unit/cli/test_note.py | 578 | CLI emitted ``{"id": null, "created": false, "error": "Creation may | STRING |
| HIGH | tests/unit/cli/test_playwright_login_render_contract.py | 709 | " 2. Or run: notebooklm auth logout && notebooklm login\n" | CODE |
| HIGH | tests/unit/cli/test_playwright_login_render_contract.py | 766 | " 2. Or run: notebooklm auth logout && notebooklm login\n" | CODE |
| HIGH | tests/unit/cli/test_playwright_login_render_contract.py | 840 | " 2. Or run: notebooklm auth logout && notebooklm login\n" | CODE |
| HIGH | tests/_guardrails/test_cassette_shapes.py | 397 | f"[null, '<inner-json>'], got {type(freq).__name__} {freq!r:.120}" | CODE |
| HIGH | tests/_guardrails/test_cassette_shapes.py | 29 | the new 9-param outer shape ``[null, "<inner-json-string>"]`` whose inner | STRING |
| HIGH | tests/_guardrails/test_cassette_shapes.py | 389 | Real chat-ask `f.req` is ``[null, "<inner-json>"]`` whose inner JSON | STRING |
| HIGH | tests/_guardrails/test_golden_decode_coverage.py | 220 | # ([] for web, [[null, true, [id]]] for drive) are pinned to ``is True`` | COMMENT |
| HIGH | tests/integration/test_vcr_comprehensive.py | 803 | # Drive sources return [[null, true, [source_id]]] when fresh | COMMENT |
| HIGH | tests/integration/test_artifacts_integration.py | 2544 | """An empty (non-null) CREATE_ARTIFACT result drifts and raises. | STRING |
| HIGH | tests/integration/test_chat_delete_conversation_vcr.py | 189 | assert params[2] is None, f"slot 2 must be null, got {params[2]!r}" | CODE |
| HIGH | tests/integration/test_vcr_example.py | 84 | 'f.req=[[["methodId",null,null,[[["notebook_id","data"]]]]]]&at=fake_csrf_token' | CODE |
| HIGH | tests/integration/test_notebooks_integration.py | 301 | # First response for rename (returns null) | COMMENT |
| HIGH | tests/integration/test_notebooks_integration.py | 334 | # Rename response (returns null) | COMMENT |
| HIGH | tests/integration/test_notebooks_integration.py | 588 | # summary-less payloads. ``[[None]]`` (summary slot explicitly null) is | COMMENT |
| HIGH | tests/integration/test_notebooks_suggest_prompts_vcr.py | 121 | assert params[4] is None, f"slot 4 must be null, got {params[4]!r}" | CODE |
| HIGH | tests/integration/test_chat_oversized_rejection_vcr.py | 8 | [["wrb.fr", null, null, null, null, [3]]] # 3 == INVALID_ARGUMENT | STRING |
| HIGH | tests/integration/test_chat_oversized_rejection_vcr.py | 10 | [["e", 4, null, null, 132]] # stream bookkeeping, NOT the error | STRING |
| HIGH⚡ | tests/integration/test_sources_integration.py | 775 | # UPDATE_SOURCE echoes null, then the hydrate fetch blows up. | COMMENT |
| HIGH⚡ | tests/integration/test_sources_integration.py | 608 | assert is_fresh is True, "Nested [null, true, ...] should mean source is fresh" | CODE |
| HIGH | tests/integration/test_sources_integration.py | 634 | # Real API returns 3 levels of nesting: [[[null, [summary], [[keywords]], []]]] | COMMENT |
| HIGH⚡ | tests/integration/test_sources_integration.py | 598 | Drive sources return [[null, true, [source_id]]] when fresh, | STRING |
| HIGH | tests/integration/test_research_cancel_vcr.py | 191 | assert params[0] is None, f"slot 0 must be null, got {params[0]!r}" | CODE |
| HIGH | tests/integration/test_research_cancel_vcr.py | 192 | assert params[1] is None, f"slot 1 must be null, got {params[1]!r}" | CODE |
| HIGH | tests/integration/cli_vcr/conftest.py | 340 | """When ``item[field]`` is present and non-null, it must be in ``allowed``.""" | STRING |
| HIGH | tests/integration/concurrency/test_chat_history_race.py | 80 | ``[null, "<params-json>"]``; the inner params list matches the order | STRING |
| HIGH | tests/integration/concurrency/test_chat_history_race.py | 650 | # Null chat POST: params[4] is null; the answer's stream id is discarded | COMMENT |
| HIGH⚡ | tests/integration/mcp_vcr/test_sources.py | 331 | issued is ``UPDATE_SOURCE`` (``b7Wfje``). The recorded echo is non-null, so | STRING |
| 19 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/cassette_patterns.py | 150 | Re-derive ``<count>`` prefixes in a chunked response body. Google's batchexecute responses are framed as alternatin | STRING |
| HIGH | tests/cassette_patterns.py | 1552 | Return a ``(status_code, body, headers)`` triple for a synthetic error. The shape is intentionally minimal; the cli | STRING |
| HIGH | src/notebooklm/_sharing.py | 148 | Share notebook with a user. Args: notebook_id: The notebook ID. email: User's email add | STRING |
| HIGH | src/notebooklm/_research.py | 317 | Start a research session. Args: notebook_id: The notebook ID. query: The research query | STRING |
| HIGH | src/notebooklm/_research.py | 496 | Poll until research reaches a terminal state or times out. When the first poll returns a concrete ``task_id``, | STRING |
| HIGH | src/notebooklm/_reqid_counter.py | 124 | Atomically increment the counter and return the new value. Args: step: Increment applied to the cou | STRING |
| HIGH | src/notebooklm/_notes.py | 93 | Get a specific note by ID. Args: notebook_id: The notebook ID. note_id: The note ID. | STRING |
| HIGH | src/notebooklm/paths.py | 219 | Get directory for a specific profile. Args: profile: Profile name. If None, resolves via resolve_profile(). | STRING |
| HIGH | src/notebooklm/client.py | 714 | Refresh authentication tokens by fetching the NotebookLM homepage. This helps prevent 'Session Expired' errors | STRING |
| HIGH | src/notebooklm/_idempotency.py | 84 | Probe-then-retry wrapper for mutating create RPCs. Args: create: Coroutine factory that issues the create R | STRING |
| HIGH | src/notebooklm/_sources.py | 151 | Get details of a specific source. Args: notebook_id: The notebook ID. source_id: The so | STRING |
| HIGH | src/notebooklm/_sources.py | 207 | Wait for a source to become ready. Polls until READY, terminal ERROR, or timeout. Configured transient | STRING |
| HIGH | src/notebooklm/_sources.py | 295 | Wait for a source to be registered server-side (status >= PROCESSING). Polls until the source is visible in the | STRING |
| HIGH | src/notebooklm/_sources.py | 345 | Wait for multiple sources to become ready in parallel. Args: notebook_id: The notebook ID. | STRING |
| HIGH | src/notebooklm/_sources.py | 429 | Add a text source (copied text) to a notebook. Args: notebook_id: The notebook ID. titl | STRING |
| HIGH | src/notebooklm/_sources.py | 483 | Add a file source to a notebook using Google's resumable upload. Registers the source, opens an upload session, | STRING |
| HIGH | src/notebooklm/_sources.py | 659 | Rename a source. Args: notebook_id: The notebook ID. source_id: The source ID to rename | STRING |
| HIGH | src/notebooklm/_sources.py | 731 | Check if a source needs to be refreshed. Args: notebook_id: The notebook ID. source_id: | STRING |
| HIGH | src/notebooklm/_sources.py | 780 | Get the full content of a source. Args: notebook_id: The notebook ID. source_id: The so | STRING |
| HIGH | src/notebooklm/_notebooks.py | 374 | Get AI-suggested prompts for a notebook. Backed by ``GeneratePromptSuggestions`` (``otmP3b``): a *general* | STRING |
| HIGH | src/notebooklm/_notebooks.py | 674 | Get notebook details. Args: notebook_id: The notebook ID. Returns: Notebook ob | STRING |
| HIGH | src/notebooklm/utils.py | 37 | Return the surrounding source-text passage for a chat citation. A :class:`~notebooklm.types.ChatReference` carries | STRING |
| HIGH | src/notebooklm/_lookup.py | 28 | Return ``obj`` unchanged, or raise ``exc`` when ``obj`` is ``None``. The narrow contract is deliberate: callers pas | STRING |
| HIGH | src/notebooklm/artifacts.py | 77 | Run an artifact-generation callable with rate-limit retry. The callable is always invoked at least once. A retry is | STRING |
| HIGH | src/notebooklm/_mind_maps_api.py | 414 | Rename a mind map (dispatches by kind: ``UPDATE_NOTE`` / ``RENAME_ARTIFACT``). Omitting ``kind`` triggers an ex | STRING |
| HIGH | src/notebooklm/_chat/notes.py | 189 | Build CREATE_NOTE params for the saved-from-chat variant. Produces the 7-element params array used by the web UI's | STRING |
| HIGH | src/notebooklm/_chat/notes.py | 291 | Save a chat answer as a citation-rich note via the saved-from-chat CREATE_NOTE variant (issue #660). Unlike the | STRING |
| HIGH | src/notebooklm/_chat/api.py | 268 | Ask the notebook a question. Args: notebook_id: The notebook ID. question: The question | STRING |
| HIGH | src/notebooklm/_chat/api.py | 817 | Read the notebook's current chat configuration. Decodes the chat-settings block from ``GET_NOTEBOOK`` so a *par | STRING |
| HIGH | src/notebooklm/_chat/api.py | 875 | Save a chat answer as a citation-rich note (issue #660). Unlike :meth:`NotesAPI.create`, this preserves the ``[ | STRING |
| HIGH | src/notebooklm/_auth/cookies.py | 164 | Extract Google cookies from Playwright storage state for NotebookLM auth. Filters through the canonical auth-domain | STRING |
| HIGH | src/notebooklm/_auth/cookies.py | 263 | Load Playwright storage state from file or environment variable. This is a shared helper used by load_auth_from_sto | STRING |
| HIGH | src/notebooklm/_auth/cookies.py | 324 | Load cookies as an httpx.Cookies object for authenticated downloads. Unlike load_auth_from_storage() which returns | STRING |
| HIGH | src/notebooklm/_auth/cookies.py | 368 | Extract Google cookies from storage state preserving original identity. Returns a path-aware ``(name, domain, path) | STRING |
| HIGH | src/notebooklm/_auth/cookies.py | 404 | Build an httpx.Cookies jar with original domains preserved. This function loads cookies from storage and creates a | STRING |
| HIGH | src/notebooklm/_auth/tokens.py | 131 | Create AuthTokens from Playwright storage state file. This is the recommended way to create AuthTokens for prog | STRING |
| HIGH | src/notebooklm/_auth/tokens.py | 248 | Load Google cookies from storage as a flat name→value dict. Loads authentication cookies with the following precede | STRING |
| HIGH | src/notebooklm/_auth/browser_capture.py | 752 | Capture NotebookLM storage state by attaching to a running Chrome over CDP. An **alternative credential source** fo | STRING |
| HIGH | src/notebooklm/_auth/extraction.py | 69 | Extract a ``WIZ_global_data[key]`` value from a NotebookLM HTML response. NotebookLM (and other Google products) em | STRING |
| HIGH | src/notebooklm/_auth/extraction.py | 207 | Extract CSRF token (SNlM0e) from NotebookLM page HTML. The CSRF token is embedded in the page's WIZ_global_dat | STRING |
| HIGH | src/notebooklm/_auth/extraction.py | 253 | Extract session ID (FdrFJe) from NotebookLM page HTML. The session ID is embedded in the page's WIZ_global_dat | STRING |
| HIGH | src/notebooklm/_auth/refresh.py | 694 | Internal: fetch CSRF and session tokens using a pre-built cookie jar. This is the single implementation for all tok | STRING |
| HIGH | src/notebooklm/_auth/refresh.py | 776 | Fetch tokens from a cookie mapping. For backward compatibility. Prefer AuthTokens.from_storage() which preserves co | STRING |
| HIGH | src/notebooklm/_auth/refresh.py | 825 | Fetch tokens with domain-preserving cookies from storage. Used by CLI helpers. Loads storage, builds jar, fetches t | STRING |
| HIGH | src/notebooklm/_auth/refresh.py | 879 | Validate the auth cookies on disk without any side effects. Performs the same token-fetch round-trip (a homepage GE | STRING |
| HIGH | src/notebooklm/_auth/account.py | 144 | Enumerate Google accounts visible to the given cookie jar. Probes ``https://notebooklm.google.com/?authuser=N`` for | STRING |
| HIGH | src/notebooklm/mcp/_resolve.py | 278 | Resolve a notebook reference (full/partial id, exact title, or unique title prefix) to its id. Args: client | STRING |
| HIGH | src/notebooklm/mcp/_resolve.py | 306 | Resolve a source reference within a notebook to its id. Args: client: The lifespan-bound client. no | STRING |
| HIGH | src/notebooklm/mcp/_resolve.py | 337 | Resolve many source references within a notebook, listing sources at most once. The per-tool callers ``chat_ask`` / | STRING |
| HIGH | src/notebooklm/mcp/_resolve.py | 398 | Resolve a note reference within a notebook to its id. Same matching rules as :func:`resolve_source`, over the noteb | STRING |
| HIGH | src/notebooklm/mcp/_resolve.py | 429 | Resolve a studio-artifact reference within a notebook to its id. Same matching rules as :func:`resolve_source`, ove | STRING |
| HIGH | src/notebooklm/cli/resolve.py | 44 | Validate and normalize an entity ID. Args: entity_id: The ID to validate. entity_name: Name for err | STRING |
| HIGH | src/notebooklm/cli/resolve.py | 126 | Get notebook ID from argument, env var, or active context. Resolution order (env-var precedence): 1. ``noteboo | STRING |
| HIGH | src/notebooklm/cli/resolve.py | 207 | Resolve a partial ID against a **pre-fetched** item list. Sync core of the partial-ID matching logic. Encapsulates | STRING |
| HIGH | src/notebooklm/cli/resolve.py | 339 | Resolve a case-insensitive partial ID prefix to a full entity ID. Allows users to type partial IDs like ``abc`` ins | STRING |
| HIGH | src/notebooklm/cli/_firefox_containers.py | 228 | Resolve a ``--browser-cookies 'firefox::<spec>'`` to a selector. Args: profile_path: Firefox profile direct | STRING |
| HIGH | src/notebooklm/cli/_firefox_containers.py | 409 | Read ``cookies.sqlite`` filtered to a single container. Args: profile_path: Firefox profile directory conta | STRING |
| HIGH | src/notebooklm/cli/_chromium_profiles.py | 316 | Read and decrypt cookies from a single Chromium user-data profile. Uses ``rookiepy.any_browser(db_path, domains=…)` | STRING |
| HIGH | src/notebooklm/cli/download_helpers.py | 34 | Resolve a partial artifact ID to a full ID. UUID-shaped IDs (canonical 8-4-4-4-12 hex layout, case-insensitive - | STRING |
| HIGH | src/notebooklm/cli/auth_runtime.py | 78 | Get auth components from context. Args: ctx: Click context with optional storage_path in obj Returns: | STRING |
| 14 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pyproject.toml | 41 | COMMENT | |
| LOW | pyproject.toml | 181 | [tool.coverage.run] | COMMENT |
| LOW | deploy/docker-compose.build.yml | 1 | # Override: build the notebooklm-mcp image from THIS checkout instead of pulling | COMMENT |
| LOW | deploy/docker-compose.yml | 1 | # Remote notebooklm-mcp behind a tunnel. Pick ONE tunnel via a Compose profile: | COMMENT |
| LOW | deploy/docker-compose.yml | 21 | # Run as YOUR uid:gid so the mounted profile's files (owned by you on the | COMMENT |
| LOW | deploy/docker-compose.yml | 41 | # tunnel egress), i.e. one global bucket. Tailscale Funnel does not set this header. | COMMENT |
| LOW | deploy/docker-compose.yml | 61 | cloudflared: | COMMENT |
| LOW | deploy/docker-compose.yml | 81 | condition: service_healthy | COMMENT |
| LOW | tests/cassette_patterns.py | 341 | # URL-encoded form for ``?authuser=`` query params. The provider- | COMMENT |
| LOW | tests/cassette_patterns.py | 361 | COMMENT | |
| LOW | tests/cassette_patterns.py | 401 | } | COMMENT |
| LOW | tests/cassette_patterns.py | 421 | # legitimate non-secret occurrence in a cassette, so collapsing to the canonical | COMMENT |
| LOW | tests/cassette_patterns.py | 441 | # scrubber would proceed to clobber the longer name. | COMMENT |
| LOW | tests/cassette_patterns.py | 461 | # token by construction and is never legitimate fixture content, so we | COMMENT |
| LOW | tests/cassette_patterns.py | 481 | # The ``JrWMbf`` leak (the NotebookLM web API key round-tripping into the | COMMENT |
| LOW | tests/cassette_patterns.py | 521 | # but they ARE real per-user values, so the bar is: NO cookie value anywhere in | COMMENT |
| LOW | tests/cassette_patterns.py | 701 | # (storage_state name-first / value-first / bare-key forms). Derived from | COMMENT |
| LOW | tests/cassette_patterns.py | 721 | # scrubbers use context-aware (callable) replacements where exact-match | COMMENT |
| LOW | tests/cassette_patterns.py | 741 | # ``ya29.`` carry floors to avoid incidental short-literal matches). | COMMENT |
| LOW | tests/cassette_patterns.py | 761 | # 2. CSRF and session tokens in WIZ_global_data (HTML / JSON responses) | COMMENT |
| LOW | tests/cassette_patterns.py | 801 | # ``authuser=<email>`` query-param form. The client appends this to | COMMENT |
| LOW | tests/cassette_patterns.py | 821 | (_EMAIL_PATTERN_BASE, "SCRUBBED_EMAIL@example.com"), | COMMENT |
| LOW | tests/cassette_patterns.py | 861 | # ``scrub_string`` (the validator would flag it but the sanitizer could | COMMENT |
| LOW | tests/cassette_patterns.py | 881 | ( | COMMENT |
| LOW | tests/cassette_patterns.py | 901 | # IDs, conversation IDs, and other internal NotebookLM identifiers. | COMMENT |
| LOW | tests/cassette_patterns.py | 921 | # allowlist a broad ``\\"[A-Z][a-z]+(?: [A-Z][a-z]+)+\\"`` regex would | COMMENT |
| LOW | tests/cassette_patterns.py | 981 | ) | COMMENT |
| LOW | tests/cassette_patterns.py | 1101 | # both raw tokens AND their canonical placeholder form so that an already- | COMMENT |
| LOW | tests/cassette_patterns.py | 1121 | # Full upload URL is rewritten to a trusted-host placeholder with a scrubbed | COMMENT |
| LOW | tests/cassette_patterns.py | 1141 | # leak (the placeholder string doesn't itself contain ``lh3.``). | COMMENT |
| LOW | tests/cassette_patterns.py | 1161 | # Double-encoded ``authuser%3D<email>`` shape detector (issue #1368). Unlike the | COMMENT |
| LOW | tests/cassette_patterns.py | 1181 | # Note Title"`` or ``alice@gmail.com`` — these shapes never match legitimate | COMMENT |
| LOW | tests/cassette_patterns.py | 1201 | # That makes the guard NECESSARY-but-not-SUFFICIENT: a credential family the | COMMENT |
| LOW | tests/cassette_patterns.py | 1221 | # JSON scalar* (``"field":"<one-pure-token>"``) — which is exactly the shape a | COMMENT |
| LOW | tests/cassette_patterns.py | 1241 | # hex literal (a 32-hex MD5 or a 36-char UUID stays well under the floor). | COMMENT |
| LOW | tests/cassette_patterns.py | 1461 | continue | COMMENT |
| LOW | tests/cassette_patterns.py | 1481 | # appear, regardless of the WIZ field name carrying them. Any surviving raw | COMMENT |
| LOW | tests/cassette_patterns.py | 1501 | # ============================================================================= | COMMENT |
| LOW | tests/vcr_config.py | 361 | # matcher's intent is to catch **structural** drift (different RPC id, different | COMMENT |
| LOW | tests/vcr_config.py | 721 | # Streaming-chat: drop the volatile conversation_id at slot 4 and | COMMENT |
| LOW | tests/vcr_config.py | 741 | c2[4] = SENTINEL | COMMENT |
| LOW | tests/vcr_config.py | 761 | # ============================================================================= | COMMENT |
| LOW | tests/vcr_config.py | 801 | # ``freq`` is wired into the default ``match_on`` tuple above. The matcher | COMMENT |
| LOW | tests/unit/test_vcr_config.py | 361 | assert out.startswith("<html>") | COMMENT |
| LOW | tests/unit/conftest.py | 41 | """ | COMMENT |
| LOW | tests/unit/conftest.py | 61 | # wrapper's ``aiter_bytes`` + rebuild path works on it. Returning a | COMMENT |
| LOW | tests/unit/test_public_api_behavior.py | 61 | NotebookNotFoundError, | COMMENT |
| LOW | tests/unit/test_cassette_sanitizer.py | 581 | allowlist = TESTS_DIR / "scripts" / "cassette_repair_allowlist.txt" | COMMENT |
| LOW | tests/unit/test_cassette_sanitizer.py | 601 | # ``artifacts_revise_slide.yaml`` is NOT in this required-set anymore | COMMENT |
| LOW | tests/unit/test_exceptions.py | 341 | with pytest.raises(SourceError): | COMMENT |
| LOW | tests/unit/test_authed_post_pipeline.py | 481 | # Tightened signature pins the explicit-collaborator contract: | COMMENT |
| LOW | tests/unit/test_auth_cookie_save_race.py | 1721 | monkeypatch.setattr(_lifecycle.asyncio, "to_thread", fake_to_thread) | COMMENT |
| LOW | tests/unit/test_user_settings_api.py | 141 | # --------------------------------------------------------------------------- | COMMENT |
| LOW | tests/unit/test_atomic_update_json.py | 321 | # Lock-path derivation contract (#1220) | COMMENT |
| LOW | tests/unit/test_playwright_domain_filter.py | 221 | def test_acceptance_sibling_domains_all_rejected_by_default(domain: str) -> None: | COMMENT |
| LOW | tests/unit/test_json_error_exit.py | 601 | # notebook create: with_client + RuntimeError -> UNEXPECTED_ERROR envelope. | COMMENT |
| LOW | tests/unit/test_rpc_health_coverage.py | 61 | # --------------------------------------------------------------------------- | COMMENT |
| LOW | tests/unit/test_rpc_health_coverage.py | 81 | "CREATE_ARTIFACT", | COMMENT |
| LOW | tests/unit/test_runtime_lifecycle.py | 121 | self._drain_tracker.run_drain_hooks = AsyncMock() | COMMENT |
| LOW | tests/unit/test_runtime_lifecycle.py | 141 | # the new loop. The ``MagicMock`` default lets both calls land | COMMENT |
| 301 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/unit/test_json_stdout_purity.py | 400 | def _setup_fs_doctor(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: | CODE |
| LOW | tests/unit/test_json_stdout_purity.py | 429 | def _setup_fs_profile_list(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: | CODE |
| LOW | tests/unit/test_auth_session_headless_reauth.py | 66 | async def update_auth_tokens(self, *, auth: AuthTokens, csrf: str, session_id: str) -> None: | CODE |
| LOW | tests/unit/test_auth_session_headless_reauth.py | 71 | def update_auth_headers(self, *, auth: AuthTokens, kernel: Any) -> None: | CODE |
| LOW | tests/unit/test_auth_cookie_save_race.py | 87 | def _set_cookie_value(jar: httpx.Cookies, name: str, value) -> None: | CODE |
| LOW | tests/unit/test_auth_session.py | 100 | async def update_auth_tokens(self, *, auth: AuthTokens, csrf: str, session_id: str) -> None: | CODE |
| LOW | tests/unit/test_auth_session.py | 105 | def update_auth_headers(self, *, auth: AuthTokens, kernel: Any) -> None: | CODE |
| LOW | tests/unit/mcp/conftest.py | 124 | __all__ = ["AsyncMock"] # re-exported for convenience in tool tests | CODE |
| LOW | tests/unit/mcp/test_entrypoint.py | 59 | def _set_oauth_env(monkeypatch: pytest.MonkeyPatch) -> None: | CODE |
| LOW | tests/unit/cli/test_cli_mcp_parity.py | 210 | def _setup(c: Any) -> None: | CODE |
| LOW | tests/unit/cli/test_cli_mcp_parity.py | 225 | def _setup(c: Any) -> None: | CODE |
| LOW | tests/_baselines/registry.py | 209 | __all__ = [ | CODE |
| LOW | tests/_baselines/__init__.py | 20 | __all__ = ["BASELINES", "Baseline"] | CODE |
| LOW | tests/integration/cli_vcr/_cassette_expectations.py | 304 | __all__ = [ | CODE |
| LOW | tests/integration/cli_vcr/conftest.py | 59 | __all__ = [ | CODE |
| LOW | tests/integration/cli_vcr/_fixtures.py | 71 | __all__ = [ | CODE |
| LOW⚡ | tests/integration/concurrency/conftest.py | 58 | logger = logging.getLogger(__name__) | CODE |
| LOW | tests/integration/concurrency/conftest.py | 210 | def set_delay(self, seconds: float) -> None: | CODE |
| LOW | tests/integration/concurrency/test_chat_history_race.py | 144 | def set_answer(self, question: str, answer: str) -> None: | CODE |
| LOW | tests/integration/concurrency/helpers.py | 47 | logger = logging.getLogger(__name__) | CODE |
| LOW | tests/integration/mcp_vcr/conftest.py | 42 | __all__ = ["build_mcp_client", "build_zero_retry_mcp_client"] | CODE |
| LOW | tests/server/fakes.py | 308 | async def set_mode(self, notebook_id: str, mode: Any) -> None: | CODE |
| LOW | tests/server/fakes.py | 724 | def set_research_failed(self, notebook_id: str, poll_id: str, *, query: str = "q") -> None: | CODE |
| LOW | tests/_fixtures/chain.py | 122 | __all__ = [ | CODE |
| LOW | tests/_fixtures/__init__.py | 22 | __all__ = [ | CODE |
| LOW | src/notebooklm/_sharing.py | 10 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_research.py | 49 | __all__ = [ | CODE |
| LOW | src/notebooklm/_research.py | 58 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/auth.py | 64 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/auth.py | 136 | __all__ = [ | CODE |
| LOW | src/notebooklm/_rpc_executor.py | 5 | __all__ = ["DecodeResponse", "RpcExecutor"] | CODE |
| LOW | src/notebooklm/_rpc_executor.py | 55 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_research_task_parser.py | 32 | __all__ = [ | CODE |
| LOW | src/notebooklm/_research_task_parser.py | 44 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_reqid_counter.py | 115 | def set_value(self, new_value: int) -> None: | CODE |
| LOW | src/notebooklm/_reqid_counter.py | 182 | __all__ = ["DEFAULT_BASELINE", "DEFAULT_STEP", "ReqidCounter"] | CODE |
| LOW | src/notebooklm/_notes.py | 28 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_request_types.py | 110 | __all__ = [ | CODE |
| LOW | src/notebooklm/_logging.py | 33 | __all__ = [ | CODE |
| LOW | src/notebooklm/config.py | 17 | __all__ = [ | CODE |
| LOW | src/notebooklm/_auth_refresh_retry.py | 168 | __all__ = ["RefreshBudget", "refresh_and_count"] | CODE |
| LOW | src/notebooklm/_serving.py | 20 | __all__ = [ | CODE |
| LOW | src/notebooklm/_transport_errors.py | 5 | __all__ = [ | CODE |
| LOW | src/notebooklm/_idempotency_policy.py | 6 | __all__ = ["register_default_policies"] | CODE |
| LOW | src/notebooklm/paths.py | 44 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/paths.py | 52 | __all__ = [ | CODE |
| LOW | src/notebooklm/paths.py | 72 | def set_active_profile(profile: str | None) -> None: | CODE |
| LOW | src/notebooklm/research.py | 19 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/research.py | 144 | __all__ = [ | CODE |
| LOW | src/notebooklm/log.py | 9 | __all__ = ["install_redaction"] | CODE |
| LOW | src/notebooklm/_streaming_post.py | 5 | __all__ = [ | CODE |
| LOW | src/notebooklm/_transport_drain.py | 54 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_transport_drain.py | 324 | __all__ = ["TransportDrainTracker", "_TransportOperationToken"] | CODE |
| LOW | src/notebooklm/client.py | 89 | __all__ = ["NotebookLMClient"] | CODE |
| LOW | src/notebooklm/client.py | 91 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_idempotency.py | 54 | logger = logging.getLogger(__name__) | CODE |
| LOW | src/notebooklm/_idempotency.py | 487 | __all__ = [ | CODE |
| LOW | src/notebooklm/io.py | 10 | __all__ = ["atomic_update_json", "atomic_write_json", "replace_file_atomically"] | CODE |
| LOW | src/notebooklm/_redact.py | 28 | __all__ = ["DEFAULT_MAX_MESSAGE", "redact"] | CODE |
| LOW | src/notebooklm/_loop_affinity.py | 62 | __all__ = ["assert_bound_loop"] | CODE |
| 206 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/unit/test_auth_cookie_policy.py | 0 | tests for auth token refresh and fetch_tokens (split in d1 pr-2). this file owns one concern from the auth subpackage. t | STRING |
| HIGH | tests/unit/test_auth_storage.py | 0 | tests for auth token refresh and fetch_tokens (split in d1 pr-2). this file owns one concern from the auth subpackage. t | STRING |
| HIGH | tests/unit/test_auth_refresh.py | 0 | tests for auth token refresh and fetch_tokens (split in d1 pr-2). this file owns one concern from the auth subpackage. t | STRING |
| HIGH | tests/unit/cli/test_artifact.py | 0 | path a: uuid-shaped id skips partial-resolve; backend none → exit 1. | STRING |
| HIGH | tests/unit/cli/test_source.py | 0 | path a: uuid-shaped id skips partial-resolve; backend none → exit 1. | STRING |
| HIGH | tests/unit/cli/test_note.py | 0 | path a: uuid-shaped id skips partial-resolve; backend none → exit 1. | STRING |
| HIGH | tests/unit/cli/test_artifact.py | 0 | path a under ``--json``: typed json error doc + exit 1. | STRING |
| HIGH | tests/unit/cli/test_source.py | 0 | path a under ``--json``: typed json error doc + exit 1. | STRING |
| HIGH | tests/unit/cli/test_note.py | 0 | path a under ``--json``: typed json error doc + exit 1. | STRING |
| HIGH | tests/unit/cli/test_artifact.py | 0 | path b: partial-resolve succeeds, backend get() returns none → exit 1. | STRING |
| HIGH | tests/unit/cli/test_source.py | 0 | path b: partial-resolve succeeds, backend get() returns none → exit 1. | STRING |
| HIGH | tests/unit/cli/test_note.py | 0 | path b: partial-resolve succeeds, backend get() returns none → exit 1. | STRING |
| HIGH | tests/_guardrails/test_server_classify_consistency.py | 0 | exactly one exemplar per category, so the parametrization is exhaustive. | STRING |
| HIGH | tests/_guardrails/test_mcp_classify_consistency.py | 0 | exactly one exemplar per category, so the parametrization is exhaustive. | STRING |
| HIGH | …_guardrails/test_classify_error_handler_consistency.py | 0 | exactly one exemplar per category, so the parametrization is exhaustive. | STRING |
| HIGH | …ts/integration/test_artifact_generation_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. | STRING |
| HIGH | tests/integration/test_research_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. | STRING |
| HIGH | tests/integration/test_side_effects_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. | STRING |
| HIGH | tests/integration/test_notes_idempotency.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. | STRING |
| HIGH | …sts/integration/concurrency/test_idempotency_create.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. | STRING |
| HIGH | …sts/integration/concurrency/test_note_create_cancel.py | 0 | extract the ``rpcids=`` query param from a batchexecute request url. | STRING |
| HIGH | tests/integration/test_settings_integration.py | 0 | integration tests for sharingapi. moved from ``tests/unit/`` to ``tests/integration/``. mock-backed (``pytest_httpx``); | STRING |
| HIGH | tests/integration/test_notes_integration.py | 0 | integration tests for sharingapi. moved from ``tests/unit/`` to ``tests/integration/``. mock-backed (``pytest_httpx``); | STRING |
| HIGH | tests/integration/test_notebooks_integration.py | 0 | integration tests for sharingapi. moved from ``tests/unit/`` to ``tests/integration/``. mock-backed (``pytest_httpx``); | STRING |
| HIGH | tests/integration/test_sharing_integration.py | 0 | integration tests for sharingapi. moved from ``tests/unit/`` to ``tests/integration/``. mock-backed (``pytest_httpx``); | STRING |
| HIGH | tests/integration/test_chat_delete_conversation_vcr.py | 0 | decode the form-encoded ``f.req`` body into its param list. | STRING |
| HIGH | tests/integration/test_notebooks_suggest_prompts_vcr.py | 0 | decode the form-encoded ``f.req`` body into its param list. | STRING |
| HIGH | tests/integration/test_research_cancel_vcr.py | 0 | decode the form-encoded ``f.req`` body into its param list. | STRING |
| HIGH | tests/integration/cli_vcr/test_generate.py | 0 | cli integration tests for note commands. these tests exercise the full cli → client → rpc path using vcr cassettes. | STRING |
| HIGH | tests/integration/cli_vcr/test_downloads.py | 0 | cli integration tests for note commands. these tests exercise the full cli → client → rpc path using vcr cassettes. | STRING |
| HIGH | tests/integration/cli_vcr/test_notes.py | 0 | cli integration tests for note commands. these tests exercise the full cli → client → rpc path using vcr cassettes. | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/cassette_patterns.py | 564 | CODE | |
| LOW | tests/vcr_config.py | 565 | CODE | |
| LOW | tests/vcr_config.py | 617 | CODE | |
| LOW | tests/unit/test_streaming_chat_wire.py | 770 | CODE | |
| LOW | tests/unit/test_concurrency_refresh_race.py | 419 | CODE | |
| LOW | tests/unit/test_sources_row_adapter.py | 72 | CODE | |
| LOW | tests/unit/test_init_order.py | 171 | CODE | |
| LOW | tests/unit/test_rpc_types.py | 23 | CODE | |
| LOW | tests/unit/test_rpc_overrides.py | 61 | CODE | |
| LOW | tests/unit/cli/test_quiet_enforcement.py | 99 | CODE | |
| LOW | tests/_guardrails/test_middleware_context_contract.py | 96 | CODE | |
| LOW | tests/_guardrails/test_no_session.py | 60 | CODE | |
| LOW | tests/_guardrails/test_cli_boundary.py | 270 | CODE | |
| LOW | tests/_guardrails/test_cli_boundary.py | 301 | CODE | |
| LOW | tests/_guardrails/test_cli_boundary.py | 334 | CODE | |
| LOW | tests/_guardrails/test_cli_boundary.py | 435 | CODE | |
| LOW | tests/_guardrails/test_cli_boundary.py | 560 | CODE | |
| LOW | tests/_guardrails/test_cli_boundary.py | 671 | CODE | |
| LOW | tests/_guardrails/test_error_handler_allowlist.py | 104 | CODE | |
| LOW | tests/_guardrails/test_public_surface.py | 205 | CODE | |
| LOW | tests/_guardrails/test_no_session_compat_bridges.py | 165 | CODE | |
| LOW | tests/_guardrails/test_type_boundaries.py | 59 | CODE | |
| LOW | tests/_guardrails/test_type_boundaries.py | 134 | CODE | |
| LOW | tests/_guardrails/test_type_boundaries.py | 162 | CODE | |
| LOW | tests/_guardrails/test_type_boundaries.py | 185 | CODE | |
| LOW | tests/_guardrails/test_cli_rpc_envelope.py | 90 | CODE | |
| LOW | tests/_guardrails/test_cli_rpc_envelope.py | 127 | CODE | |
| LOW | tests/_guardrails/test_cli_rpc_envelope.py | 178 | CODE | |
| LOW | tests/_guardrails/test_server_boundary.py | 44 | CODE | |
| LOW | tests/_guardrails/test_app_boundary.py | 116 | CODE | |
| LOW | tests/_guardrails/test_golden_decode_coverage.py | 318 | CODE | |
| LOW | …sts/_guardrails/test_no_inline_deprecation_warnings.py | 53 | CODE | |
| LOW | tests/_guardrails/test_mcp_boundary.py | 46 | CODE | |
| LOW | tests/_guardrails/test_session_runtime_boundaries.py | 164 | CODE | |
| LOW | tests/_guardrails/test_session_runtime_boundaries.py | 261 | CODE | |
| LOW | tests/_guardrails/test_session_runtime_boundaries.py | 331 | CODE | |
| LOW | tests/_guardrails/test_no_cross_test_imports.py | 50 | CODE | |
| LOW | tests/_guardrails/test_client_composition.py | 96 | CODE | |
| LOW | tests/_guardrails/test_public_surface_manifest.py | 590 | CODE | |
| LOW | …sts/_guardrails/test_no_legacy_rpc_callable_aliases.py | 73 | CODE | |
| LOW | …sts/_guardrails/test_no_legacy_rpc_callable_aliases.py | 95 | CODE | |
| LOW | tests/_guardrails/test_install_docs.py | 88 | CODE | |
| LOW | tests/_guardrails/test_install_docs.py | 215 | CODE | |
| LOW | tests/_guardrails/test_source_policy_parity.py | 42 | CODE | |
| LOW | tests/_guardrails/test_rpc_method_ids_only_in_types.py | 47 | CODE | |
| LOW | tests/_guardrails/test_rpc_method_ids_only_in_types.py | 91 | CODE | |
| LOW | tests/_guardrails/test_no_core_imports.py | 73 | CODE | |
| LOW | tests/_guardrails/test_no_bare_tests_root_imports.py | 55 | CODE | |
| LOW | tests/_guardrails/test_asyncio_loop_affinity_guard.py | 283 | CODE | |
| LOW | tests/integration/test_research_deep_poll_vcr.py | 370 | CODE | |
| LOW | tests/integration/test_research_deep_poll_vcr.py | 485 | CODE | |
| LOW | tests/integration/test_sources_integration.py | 1817 | CODE | |
| LOW | tests/integration/cli_vcr/_cassette_expectations.py | 117 | CODE | |
| LOW | tests/integration/cli_vcr/_cassette_expectations.py | 178 | CODE | |
| LOW | tests/integration/concurrency/helpers.py | 55 | CODE | |
| LOW | tests/integration/mcp_vcr/test_artifacts.py | 73 | CODE | |
| LOW | tests/server/fakes.py | 192 | CODE | |
| LOW | tests/scripts/check_cassettes_clean.py | 226 | CODE | |
| LOW | tests/scripts/compress_polling_cassette.py | 120 | CODE | |
| LOW | tests/e2e/conftest.py | 527 | CODE | |
| 93 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/unit/test_windows_compatibility.py | 315 | except Exception as e: | CODE |
| LOW | tests/unit/cli/test_cli_mcp_parity.py | 171 | except Exception as exc: # noqa: BLE001 - caller classifies (see _mcp_capture) | CODE |
| LOW | tests/unit/concurrency/test_close_cancellation_leak.py | 368 | except Exception: | CODE |
| LOW | tests/integration/test_chat_delete_conversation_vcr.py | 139 | except Exception as exc: # noqa: BLE001 | CODE |
| LOW⚡ | tests/integration/test_chat_multi_source_vcr.py | 272 | except Exception as exc: # noqa: BLE001 — best-effort cleanup | CODE |
| LOW | tests/integration/test_chat_oversized_rejection_vcr.py | 142 | except Exception: # noqa: BLE001 — best-effort scratch cleanup | CODE |
| LOW | …sts/integration/test_curl_cffi_cassette_conformance.py | 66 | except Exception: | CODE |
| LOW | tests/integration/test_research_cancel_vcr.py | 152 | except Exception as exc: # noqa: BLE001 | CODE |
| LOW | tests/integration/concurrency/test_add_file_toctou.py | 235 | except Exception: # noqa: BLE001 — defensive | CODE |
| LOW | tests/scripts/setup-generation-notebook.py | 74 | except Exception as exc: # noqa: BLE001 — top-level CLI surface | CODE |
| LOW | tests/scripts/check_cassettes_clean.py | 196 | except Exception: # noqa: BLE001 - degrade to line-scan on any parse/read error | CODE |
| MEDIUM | tests/e2e/conftest.py | 98 | def _ask_with_skip(*args, **kwargs): | CODE |
| LOW⚡ | tests/e2e/conftest.py | 688 | except Exception as e: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 749 | except Exception: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 864 | except Exception: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 873 | except Exception: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 942 | except Exception as e: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 968 | except Exception: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 986 | except Exception: | CODE |
| LOW⚡ | tests/e2e/conftest.py | 1138 | except Exception as e: | CODE |
| LOW | tests/e2e/conftest.py | 829 | except Exception: | CODE |
| LOW | tests/e2e/conftest.py | 831 | except Exception: | CODE |
| LOW | tests/e2e/conftest.py | 842 | except Exception: | CODE |
| LOW | tests/e2e/conftest.py | 844 | except Exception: | CODE |
| LOW | tests/e2e/conftest.py | 1076 | except Exception: | CODE |
| LOW | tests/e2e/conftest.py | 1078 | except Exception: | CODE |
| LOW | tests/e2e/test_downloads.py | 133 | except Exception: | CODE |
| LOW | docs/rpc-reference.md | 155 | except Exception: | CODE |
| LOW⚡ | examples/notes.py | 157 | except Exception as e: | CODE |
| LOW | examples/bulk-import.py | 64 | except Exception as e: | STRING |
| LOW | examples/bulk-import.py | 75 | except Exception as e: | STRING |
| LOW | examples/bulk-import.py | 86 | except Exception as e: | STRING |
| LOW⚡ | examples/chat.py | 102 | except Exception as e: | CODE |
| LOW | scripts/diagnose_get_notebook.py | 130 | except Exception as e: | CODE |
| LOW | scripts/mcp_live_smoke.py | 195 | except Exception as exc: # noqa: BLE001 - top-level smoke wants one clean line | CODE |
| LOW | scripts/audit_public_api_compat.py | 217 | except Exception: | CODE |
| LOW | scripts/audit_public_api_compat.py | 402 | except Exception as exc: | CODE |
| LOW | scripts/_onetime_rescrub_cookies.py | 129 | except Exception as exc: # noqa: BLE001 | CODE |
| LOW | scripts/check_rpc_health.py | 1224 | except Exception as e: | CODE |
| LOW | scripts/check_rpc_health.py | 1240 | except Exception as e: | CODE |
| LOW | scripts/check_rpc_health.py | 1256 | except Exception as e: | CODE |
| LOW | scripts/check_rpc_health.py | 1265 | except Exception as e: | CODE |
| LOW | src/notebooklm/_auth_refresh_retry.py | 147 | except Exception as refresh_error: | CODE |
| LOW | src/notebooklm/client.py | 529 | except Exception as close_exc: | CODE |
| LOW | src/notebooklm/_curl_cffi_transport.py | 155 | except Exception: # noqa: BLE001 — cleanup must not mask the original error | CODE |
| LOW | src/notebooklm/_notebooks.py | 536 | except Exception: | CODE |
| LOW | src/notebooklm/_notebooks.py | 585 | except Exception: | CODE |
| LOW | src/notebooklm/_notebooks.py | 630 | except Exception: | CODE |
| LOW | src/notebooklm/_notebooks.py | 644 | except Exception: | CODE |
| MEDIUM | src/notebooklm/_notebooks.py | 543 | def _create() -> Notebook: | CODE |
| LOW | src/notebooklm/_client_metrics.py | 143 | except Exception as exc: # noqa: BLE001 - observability must not alter behavior | CODE |
| LOW | src/notebooklm/_atomic_io.py | 280 | except Exception: | CODE |
| LOW | src/notebooklm/_atomic_io.py | 284 | except Exception as cleanup_err: | CODE |
| LOW | src/notebooklm/_note_service.py | 367 | except Exception: # noqa: BLE001 — log and proceed to delete | CODE |
| LOW | src/notebooklm/_note_service.py | 410 | except Exception: # noqa: BLE001 — best-effort cleanup, must not surface | CODE |
| LOW | src/notebooklm/_middleware/metrics.py | 104 | except Exception as exc: | CODE |
| LOW | src/notebooklm/_middleware/tracing.py | 113 | except Exception as exc: | CODE |
| LOW | src/notebooklm/_chat/wire.py | 487 | except Exception: | CODE |
| LOW | src/notebooklm/_auth/headless_reauth.py | 660 | except Exception as exc: # noqa: BLE001 - recovery is best-effort | CODE |
| LOW | src/notebooklm/_auth/psidts_recovery.py | 459 | except Exception as exc: # noqa: BLE001 - persistence failure is non-fatal here | CODE |
| 57 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/cassette_patterns.py | 209 | # fixed number of characters) is robust to alternate-length prefixes if | COMMENT |
| MEDIUM | tests/vcr_config.py | 366 | # matcher robust across recording sessions while still failing on meaningful | COMMENT |
| MEDIUM | tests/vcr_config.py | 748 | # or extra non-volatile dict keys — while staying robust against leaf | COMMENT |
| MEDIUM⚡ | tests/unit/test_json_stdout_purity.py | 390 | # so the mock-client harness can't drive them. The parametrized sweep | COMMENT |
| MEDIUM | tests/unit/test_json_stdout_purity.py | 632 | # the mock-client harness. `notebook_create` goes through the standard | COMMENT |
| MEDIUM | tests/unit/test_json_stdout_purity.py | 657 | # harness — they read NOTEBOOKLM_HOME directly. Stage a clean layout | COMMENT |
| MEDIUM | tests/unit/test_auth_cookie_save_race.py | 1738 | # order) to stay robust across schedulers. | COMMENT |
| MEDIUM⚡ | tests/unit/test_row_adapters.py | 1062 | # TZ-robust: compare the round-tripped epoch, not a wall-time string. | COMMENT |
| MEDIUM⚡ | tests/unit/test_json_error_exit.py | 704 | # then invoke without the mock-client harness (these commands don't | COMMENT |
| MEDIUM | tests/unit/test_json_error_exit.py | 679 | # Filesystem-driven failure cases bypass the mock-client harness — they | COMMENT |
| MEDIUM | tests/unit/test_sources_upload_delegation.py | 52 | # parse bytecode. ``casefold()`` makes the suffix check robust on | COMMENT |
| MEDIUM | tests/unit/test_rpc_golden_payloads.py | 1237 | # Wiring those would require contorting the harness or adding production code | COMMENT |
| MEDIUM⚡ | tests/unit/test_vcr_body_matcher.py | 293 | # Volatile-key stripping — keep matching robust across recording timestamps | COMMENT |
| MEDIUM | tests/unit/app/test_app_doctor.py | 351 | # The ``browser`` extra is installed in CI; pin it so the test is robust | COMMENT |
| MEDIUM | tests/unit/cli/test_login.py | 309 | # rebuilding this harness. | COMMENT |
| MEDIUM | tests/_guardrails/test_no_pinned_cassette_values.py | 170 | # allow-listing the comparison methods) keeps the gate robust as new | COMMENT |
| MEDIUM | tests/integration/concurrency/test_harness_smoke.py | 50 | # concurrency-harness smoke tests against a mock transport; no | COMMENT |
| MEDIUM | tests/integration/concurrency/test_harness_smoke.py | 139 | # Peak in-flight was high — the harness is genuinely fanning out. | COMMENT |
| MEDIUM | …ts/integration/concurrency/test_max_concurrent_rpcs.py | 71 | # concurrency-harness tests against a mock transport; no HTTP, | COMMENT |
| MEDIUM | …ts/integration/concurrency/test_max_concurrent_rpcs.py | 156 | # Anything <= 8 would mean the gate isn't kicking in (the harness | COMMENT |
| MEDIUM | tests/integration/mcp_vcr/test_research.py | 54 | # recorded poll (the "Python programming best practices" task). Pinning it makes | COMMENT |
| MEDIUM | tests/e2e/test_cli_live.py | 49 | # or an enveloped object so the test is robust to the exact shape. | COMMENT |
| MEDIUM⚡ | tests/e2e/test_research_import_verification.py | 92 | # makes this path robust for fast-mode imports too. | COMMENT |
| MEDIUM | scripts/capture_rpc_registry.py | 78 | # is robust to nested ``[...]`` in the options array (a single forward regex | COMMENT |
| LOW | src/notebooklm/_sharing.py | 128 | # Fetch current status and override view_level with what we just set | COMMENT |
| MEDIUM | src/notebooklm/_atomic_io.py | 355 | # and re-introduce the divergent-lock race. ``casefold`` is the robust | COMMENT |
| MEDIUM | src/notebooklm/_app/doctor.py | 116 | # Reuse the shared, name-robust extractor (drops non-dict rows and | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/unit/test_cookie_domain_split.py | 400 | # Step 2: storage_state → DomainCookieMap (used by AuthTokens). | COMMENT |
| LOW⚡ | tests/unit/test_cookie_domain_split.py | 406 | # Step 3: storage_state → httpx jar (used by downloads + refresh). | COMMENT |
| LOW⚡ | tests/unit/test_cookie_domain_split.py | 414 | # Step 4: the runtime gate itself accepts every YouTube variant. | COMMENT |
| LOW | tests/unit/test_cookie_domain_split.py | 389 | # Step 1: rookiepy → storage_state conversion must keep the YouTube | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 814 | # Step 0: register_file_source captures a baseline GET_NOTEBOOK | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 817 | # Step 1: Mock RPC registration response (o4cbdc) | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 827 | # Step 2: Mock upload session start response | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 837 | # Step 3: Mock upload finalize response | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1031 | # Step 0: Baseline GET_NOTEBOOK for the register_file_source probe wrapper. | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1033 | # Step 1: Mock RPC registration | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1043 | # Step 2: Mock upload session start | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1053 | # Step 3: Mock upload finalize | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 2791 | # Step 0: Baseline GET_NOTEBOOK for the register_file_source probe wrapper. | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 2793 | # Step 1: Successful RPC registration | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 2803 | # Step 2: Upload session response WITHOUT the required upload URL header | COMMENT |
| LOW⚡ | tests/e2e/test_research_import_verification.py | 42 | # Step 1: Start fast web research | COMMENT |
| LOW | tests/e2e/test_research_import_verification.py | 56 | # Step 2: Poll until complete | COMMENT |
| LOW | tests/e2e/test_research_import_verification.py | 74 | # Step 3: Get sources to import | COMMENT |
| LOW⚡ | tests/e2e/test_research_import_verification.py | 88 | # Step 4: Import sources. Use the verification-aware variant so an | COMMENT |
| LOW⚡ | tests/e2e/test_research_import_verification.py | 99 | # Step 5: Poll for imported sources to appear | COMMENT |
| LOW | tests/e2e/test_research_import_verification.py | 111 | # Step 6: Verify source count | COMMENT |
| LOW⚡ | tests/e2e/test_research.py | 196 | # Step 1: Start research | COMMENT |
| LOW | tests/e2e/test_research.py | 207 | # Step 2: Poll until complete | COMMENT |
| LOW | tests/e2e/test_research.py | 229 | # Step 3: Import sources (if any found) | COMMENT |
| LOW⚡ | docs/troubleshooting.md | 468 | # Step 1: Install bird (macOS/Linux) | COMMENT |
| LOW⚡ | docs/troubleshooting.md | 471 | # Step 2: Fetch X.com content as markdown | COMMENT |
| LOW⚡ | docs/troubleshooting.md | 474 | # Step 3: Add the local markdown file to NotebookLM | COMMENT |
| LOW | docs/rpc-development.md | 250 | ### Step 1: Capture | COMMENT |
| LOW | docs/rpc-development.md | 259 | ### Step 2: Decode | COMMENT |
| LOW | docs/rpc-development.md | 277 | ### Step 3: Implement | COMMENT |
| LOW | docs/rpc-development.md | 326 | ### Step 4: Test | COMMENT |
| LOW | docs/rpc-development.md | 357 | ### Step 5: Document | COMMENT |
| LOW | examples/video.py | 26 | # Step 1: Create a notebook with content | COMMENT |
| LOW⚡ | examples/video.py | 45 | # Step 2: Generate the video overview | COMMENT |
| LOW | examples/video.py | 80 | # Step 3: Wait for completion with status updates | COMMENT |
| LOW | examples/video.py | 95 | # Step 4: Download the video | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/unit/test_cassette_sanitizer.py | 532 | fake_key = "AIza" + "Z" * 35 | CODE |
| LOW⚡ | tests/unit/test_cassette_sanitizer.py | 535 | (examples / "leak.json").write_text(f'{{"JrWMbf":"{fake_key}"}}\n', encoding="utf-8") | CODE |
| LOW⚡ | tests/unit/test_chat_ask_invariants.py | 168 | account_email="user@example.com", | CODE |
| LOW | tests/unit/test_chat_ask_invariants.py | 189 | assert _extract_query_param(str(request.url), "authuser") == "user@example.com" | CODE |
| LOW⚡ | tests/unit/test_artifact_generation_prompt.py | 101 | raw = _row_with_prompt(ArtifactTypeCode.AUDIO.value, 6, (1, 0), prompt="placeholder") | CODE |
| LOW⚡ | tests/unit/test_token_regex.py | 47 | assert value == "user@example.com" | CODE |
| LOW⚡ | tests/unit/test_cassette_patterns.py | 258 | @pytest.mark.parametrize("placeholder", sorted(SCRUB_PLACEHOLDERS)) | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 16 | data = ["user@example.com", 3, [], ["Test User", "https://avatar.url"]] | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 19 | assert user.email == "user@example.com" | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 43 | data = ["user@example.com", 2, []] | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 46 | assert user.email == "user@example.com" | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 53 | data = ["user@example.com", 99, []] | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 60 | data = ["user@example.com", {"permission": 3}, []] | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 106 | data = ["user@example.com", 3, [], ["Just Name"]] | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 243 | ["user@example.com", 3, [], ["Name", "https://avatar"]], # full | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 244 | ["user@example.com", 2, []], # minimal (no user_info slot) | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 245 | ["user@example.com", 3, [], ["Just Name"]], # partial user_info (no avatar) | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 246 | ["user@example.com", 99, []], # unknown permission | CODE |
| LOW⚡ | tests/unit/test_sharing_types.py | 247 | ["user@example.com", {"k": 1}, []], # malformed (unhashable) permission | CODE |
| LOW⚡ | tests/unit/app/test_app_sharing.py | 129 | "user@example.com", | CODE |
| LOW⚡ | tests/unit/app/test_app_sharing.py | 139 | "user@example.com", | CODE |
| LOW⚡ | tests/unit/app/test_app_sharing.py | 159 | "user@example.com", | CODE |
| LOW⚡ | tests/unit/app/test_app_sharing.py | 166 | "full_nb_part", "user@example.com", SharePermission.VIEWER | CODE |
| LOW⚡ | tests/unit/app/test_app_sharing.py | 181 | await execute_share_remove_user(client, "nb_1", "user@example.com") | CODE |
| LOW⚡ | tests/unit/app/test_app_sharing.py | 183 | client.sharing.remove_user.assert_awaited_once_with("nb_1", "user@example.com") | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 166 | ["share", "add", "user@example.com", "-n", "nb_123"], | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 175 | "user@example.com", | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 196 | ["share", "add", "user@example.com", "-n", "nb_123", "-p", "editor"], | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 204 | "user@example.com", | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 232 | ["share", "remove", "user@example.com", "-n", "nb_123", "-y"], | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 238 | mock_client.sharing.remove_user.assert_called_once_with("nb_123", "user@example.com") | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 279 | ["share", "remove", "user@example.com", "-n", "nb_123", "--json"], | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 284 | assert '"removed_user": "user@example.com"' in result.output | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 304 | ["share", "remove", "user@example.com", "-n", "nb_123", "--json"], | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 309 | assert '"removed_user": "user@example.com"' in result.output | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 311 | mock_client.sharing.remove_user.assert_called_once_with("nb_123", "user@example.com") | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 477 | assert '"updated_user": "user@example.com"' in result.output | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 548 | ["share", "add", "user@example.com", "-n", "nb_123", "--json"], | CODE |
| LOW⚡ | tests/unit/cli/test_share.py | 553 | assert '"added_user": "user@example.com"' in result.output | CODE |
| LOW | tests/unit/cli/test_share.py | 255 | ["share", "remove", "user@example.com", "-n", "nb_123"], | CODE |
| LOW | tests/unit/cli/test_share.py | 437 | ["share", "update", "user@example.com", "-n", "nb_123", "-p", "editor"], | CODE |
| LOW | tests/unit/cli/test_share.py | 445 | "nb_123", "user@example.com", SharePermission.EDITOR | CODE |
| LOW | tests/unit/cli/test_share.py | 466 | "user@example.com", | CODE |
| LOW⚡ | tests/unit/cli/test_session_characterization.py | 200 | result = char_runner.invoke(cli, ["login", "--account", "user@example.com"]) | CODE |
| LOW | tests/integration/test_sharing_integration.py | 342 | ["user@example.com", 2, [], ["User", "https://avatar"]], # Now editor | CODE |
| LOW | tests/integration/test_sharing_integration.py | 353 | "user@example.com", | CODE |
| LOW | tests/server/fakes.py | 657 | self.account_email: str | None = "user@example.com" | CODE |
| LOW | tests/server/test_meta.py | 29 | self.details = {"account": {"email": "user@example.com", "authuser": 0}} | CODE |
| LOW⚡ | tests/server/test_meta.py | 186 | assert account["email"] == "user@example.com" | CODE |
| LOW⚡ | tests/server/test_meta.py | 199 | assert account["email"] == "user@example.com" | CODE |
| LOW | tests/server/test_meta.py | 225 | assert account["email"] == "user@example.com" | CODE |
| LOW | docs/rpc-reference.md | 1995 | await client.sharing.add_user(notebook_id, "user@example.com", SharePermission.VIEWER) | CODE |
| LOW⚡ | scripts/check_rpc_health.py | 571 | return [[notebook_id], [["placeholder"]]] | CODE |
| LOW⚡ | scripts/check_rpc_health.py | 574 | return [[notebook_id], "placeholder", "New Title"] | CODE |
| LOW⚡ | scripts/check_rpc_health.py | 582 | return [[notebook_id], "placeholder"] | CODE |
| LOW⚡ | scripts/check_rpc_health.py | 585 | return [[notebook_id], "placeholder", "New Name"] | CODE |
| LOW⚡ | scripts/check_rpc_health.py | 588 | return [[notebook_id], "placeholder", 1] | CODE |
| LOW | scripts/check_rpc_health.py | 611 | return [[notebook_id], "placeholder", "Updated", "Updated content"] | CODE |
| LOW | scripts/check_rpc_health.py | 619 | return [[notebook_id], "placeholder", True] | CODE |
| LOW | src/notebooklm/_sharing.py | 30 | "user@example.com", | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/unit/test_concurrency_refresh_race.py | 79 | # Test-side deadline for any single asyncio.Event in the race scaffolding. | COMMENT |
| MEDIUM | tests/unit/test_concurrency_refresh_race.py | 460 | # Build the same auth scaffold the unit conftest's ``make_core`` produces | COMMENT |
| MEDIUM⚡ | tests/unit/test_check_rpc_health.py | 573 | # The streamed-chat orchestration RPC has no obfuscated method ID to echo back | COMMENT |
| MEDIUM⚡ | tests/unit/test_drive_import.py | 436 | # DriveImportService — orchestration, cleanup, second defense | COMMENT |
| MEDIUM | tests/unit/test_rpc_health_coverage.py | 118 | # without re-introducing the whole skip-list scaffolding. | COMMENT |
| MEDIUM⚡ | tests/unit/test_chat_transport.py | 50 | # Test scaffolding | COMMENT |
| MEDIUM⚡ | tests/unit/app/test_app_source_clean.py | 236 | # run_source_clean — orchestration | COMMENT |
| MEDIUM | tests/cassettes/cli_notebook_create.yaml | 2158 | SEO 2026:The Ultimate Roadmap to AI Agent Visibility - Karan Powar\\\",[null,3905,[1773505574,851569000],[\\\"0f | CODE |
| MEDIUM | tests/cassettes/cli_notebook_create.yaml | 2159 | SEO 2026:The Ultimate Roadmap to AI Agent Visibility - Karan Powar\\\",[null,3905,[1773505614,213638000],[\\\"10 | CODE |
| MEDIUM | tests/cassettes/workflow_tracer_bullet.yaml | 2086 | SEO 2026:The Ultimate Roadmap to AI Agent Visibility - Karan Powar\\\",[null,3905,[1773505574,851569000],[\\\"0f | CODE |
| MEDIUM | tests/cassettes/workflow_tracer_bullet.yaml | 2087 | SEO 2026:The Ultimate Roadmap to AI Agent Visibility - Karan Powar\\\",[null,3905,[1773505614,213638000],[\\\"10 | CODE |
| MEDIUM | tests/cassettes/cli_notebook_list.yaml | 2158 | SEO 2026:The Ultimate Roadmap to AI Agent Visibility - Karan Powar\\\",[null,3905,[1773505574,851569000],[\\\"0f | CODE |
| MEDIUM | tests/cassettes/cli_notebook_list.yaml | 2159 | SEO 2026:The Ultimate Roadmap to AI Agent Visibility - Karan Powar\\\",[null,3905,[1773505614,213638000],[\\\"10 | CODE |
| MEDIUM | docs/architecture.md | 1079 | │ ├── source_clean.py # Click-free `source clean` core: junk-source classification + batched-deletion orchest | CODE |
| MEDIUM | docs/adr/0013-composable-session-capabilities.md | 254 | - **C-Z. AST-based delegate-surface regression guard** *(historical)* — introduced by PR [#885](https://github.com/teng- | CODE |
| MEDIUM | docs/adr/0013-composable-session-capabilities.md | 254 | - **C-Z. AST-based delegate-surface regression guard** *(historical)* — introduced by PR [#885](https://github.com/teng- | CODE |
| MEDIUM | docs/adr/0014-feature-local-runtime-adapters.md | 208 | ### Rule 4 — `Session` retains only orchestration plus the documented middleware-chain seams | COMMENT |
| MEDIUM | scripts/check_rpc_health.py | 1349 | # Probe the streamed-chat orchestration RPC. It is not an | COMMENT |
| MEDIUM | src/notebooklm/_notebook_payloads.py | 30 | # what the backend RETURNS for each mode. ``5`` critique / ``6`` debate scaffolding / | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/unit/test_cookie_domain_split.py | 400 | # Step 2: storage_state → DomainCookieMap (used by AuthTokens). | COMMENT |
| LOW⚡ | tests/unit/test_cookie_domain_split.py | 406 | # Step 3: storage_state → httpx jar (used by downloads + refresh). | COMMENT |
| LOW⚡ | tests/unit/test_cookie_domain_split.py | 414 | # Step 4: the runtime gate itself accepts every YouTube variant. | COMMENT |
| LOW | tests/unit/test_cookie_domain_split.py | 389 | # Step 1: rookiepy → storage_state conversion must keep the YouTube | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 814 | # Step 0: register_file_source captures a baseline GET_NOTEBOOK | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 817 | # Step 1: Mock RPC registration response (o4cbdc) | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 827 | # Step 2: Mock upload session start response | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 837 | # Step 3: Mock upload finalize response | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1031 | # Step 0: Baseline GET_NOTEBOOK for the register_file_source probe wrapper. | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1033 | # Step 1: Mock RPC registration | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1043 | # Step 2: Mock upload session start | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 1053 | # Step 3: Mock upload finalize | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 2791 | # Step 0: Baseline GET_NOTEBOOK for the register_file_source probe wrapper. | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 2793 | # Step 1: Successful RPC registration | COMMENT |
| LOW⚡ | tests/integration/test_sources_integration.py | 2803 | # Step 2: Upload session response WITHOUT the required upload URL header | COMMENT |
| LOW⚡ | tests/e2e/test_research_import_verification.py | 42 | # Step 1: Start fast web research | COMMENT |
| LOW | tests/e2e/test_research_import_verification.py | 56 | # Step 2: Poll until complete | COMMENT |
| LOW | tests/e2e/test_research_import_verification.py | 74 | # Step 3: Get sources to import | COMMENT |
| LOW⚡ | tests/e2e/test_research_import_verification.py | 88 | # Step 4: Import sources. Use the verification-aware variant so an | COMMENT |
| LOW⚡ | tests/e2e/test_research_import_verification.py | 99 | # Step 5: Poll for imported sources to appear | COMMENT |
| LOW | tests/e2e/test_research_import_verification.py | 111 | # Step 6: Verify source count | COMMENT |
| LOW⚡ | tests/e2e/test_research.py | 196 | # Step 1: Start research | COMMENT |
| LOW | tests/e2e/test_research.py | 207 | # Step 2: Poll until complete | COMMENT |
| LOW | tests/e2e/test_research.py | 229 | # Step 3: Import sources (if any found) | COMMENT |
| LOW | examples/video.py | 26 | # Step 1: Create a notebook with content | COMMENT |
| LOW⚡ | examples/video.py | 45 | # Step 2: Generate the video overview | COMMENT |
| LOW | examples/video.py | 80 | # Step 3: Wait for completion with status updates | COMMENT |
| LOW | examples/video.py | 95 | # Step 4: Download the video | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/unit/test_artifact_downloads.py | 756 | # Create the complex nested structure for data table. | COMMENT |
| MEDIUM⚡ | tests/unit/test_sources_upload.py | 937 | # Create a temp file | COMMENT |
| MEDIUM⚡ | tests/unit/cli/test_resolve.py | 333 | # Create a notebook with a short ID that we'll match exactly | COMMENT |
| MEDIUM | tests/unit/cli/test_source.py | 360 | # Create a temp file | COMMENT |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 760 | # Create a test file | COMMENT |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 850 | # Create a source to delete | COMMENT |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 888 | # Create a notebook to delete | COMMENT |
| MEDIUM | tests/integration/test_vcr_comprehensive.py | 920 | # Create a note to delete | COMMENT |
| MEDIUM | tests/e2e/test_artifacts.py | 246 | # Create a quiz artifact for deletion (different type than flashcards) | COMMENT |
| MEDIUM | tests/e2e/test_sources.py | 121 | # Create a source to delete | COMMENT |
| MEDIUM⚡ | examples/notes.py | 49 | # Create a new note with title and content | COMMENT |
| MEDIUM | examples/notes.py | 27 | # Create a notebook for our examples | COMMENT |
| MEDIUM | examples/chat.py | 23 | # Create a notebook with some content | COMMENT |
| MEDIUM | src/notebooklm/notebooklm_cli.py | 170 | notebooklm create "My Notes" # Create a notebook | STRING |
| MEDIUM | src/notebooklm/_auth/browser_capture.py | 839 | # Create a TEMPORARY page we own for the navigation, and close ONLY | COMMENT |
| MEDIUM | src/notebooklm/_auth/master_token.py | 299 | # Create the temp at 0600 from the start (don't widen-then-chmod) — this is a | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL⚡ | tests/unit/mcp/test_studio.py | 1160 | assert mock_client.artifacts.download_quiz.await_args.kwargs.get("output_format") == "markdown" | CODE |
| CRITICAL | tests/unit/cli/test_generate.py | 820 | mock_client.mind_maps.generate.await_args.kwargs.get("instructions") | CODE |
| CRITICAL | tests/unit/cli/test_resolver_characterization.py | 400 | passed_conv_id = mock_client.chat.ask.await_args.kwargs.get("conversation_id") | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | docs/python-api.md | 989 | # Get AI-generated description (parsed with suggested topics) | COMMENT |
| HIGH | docs/python-api.md | 1082 | # Get AI-generated summary and keywords (returns a typed SourceGuide) | COMMENT |
| HIGH⚡ | examples/video.py | 51 | # - CINEMATIC: AI-generated documentary footage (Veo 3) | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/unit/test_windows_compatibility.py | 264 | # Check if UTF-8 mode is active (either via flag or env var) | COMMENT |
| LOW | tests/unit/test_auth_storage.py | 137 | # Set NOTEBOOKLM_HOME to tmp_path and create a file there | COMMENT |
| LOW⚡ | tests/e2e/test_chat.py | 68 | # Check if any references have cited_text | COMMENT |
| LOW | scripts/check_rpc_health.py | 695 | # Check if error response still contains our expected ID | COMMENT |
| LOW | scripts/check_rpc_health.py | 712 | # Check if expected ID is in response | COMMENT |
| LOW | .github/workflows/nightly.yml | 56 | # Check if it's main or a release branch | COMMENT |
| LOW | src/notebooklm/_auth/refresh.py | 748 | # Check if we were redirected to login | COMMENT |
| LOW | src/notebooklm/_auth/cookie_policy.py | 592 | # Check if it's a valid Google domain (base or regional) | COMMENT |
| LOW | src/notebooklm/_auth/cookie_policy.py | 606 | # Check if domain is a subdomain of allowed suffixes | COMMENT |
| LOW | src/notebooklm/_app/chat.py | 121 | # Check if the user switched notebooks via the --notebook flag. Only read | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/_helpers/client_factory.py | 29 | CODE | |
| LOW | tests/unit/test_rpc_golden_payloads.py | 1457 | CODE | |
| LOW | tests/unit/cli/test_playwright_login_render_contract.py | 150 | CODE | |
| LOW | src/notebooklm/_client_assembly.py | 92 | CODE | |
| LOW | src/notebooklm/client.py | 157 | CODE | |
| LOW | src/notebooklm/client.py | 594 | CODE | |
| LOW | src/notebooklm/exceptions.py | 411 | CODE | |
| LOW | src/notebooklm/mcp/tools/studio.py | 276 | CODE | |
| LOW | src/notebooklm/mcp/tools/sources.py | 438 | CODE | |
| LOW | src/notebooklm/mcp/tools/sources.py | 625 | CODE | |
| LOW | src/notebooklm/cli/resolve.py | 192 | CODE | |
| LOW | src/notebooklm/_runtime/init.py | 481 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/server/conftest.py | 67 | CODE | |
| MEDIUM | tests/server/test_app_lifecycle.py | 111 | CODE | |
| MEDIUM | tests/server/test_sources.py | 90 | CODE |