============================= test session starts ==============================
platform darwin -- Python 3.12.11, pytest-9.1.1, pluggy-1.6.0 -- /Users/zhengbingjin/Project/Github/MYIA/.venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/zhengbingjin/Project/Github/MYIA
configfile: pyproject.toml
plugins: anyio-4.15.1
collecting ... collected 329 items

tests/test_push_images.py::test_clip_text_short_passthrough_and_ellipsis_counts_into_limit PASSED [  0%]
tests/test_push_images.py::test_clip_text_limit_below_one_degrades_to_ellipsis PASSED [  0%]
tests/test_push_images.py::test_item_images_reads_metadata_merges_and_filters_missing_files PASSED [  0%]
tests/test_push_images.py::test_item_images_supports_object_items_with_metadata_attr PASSED [  1%]
tests/test_push_images.py::test_item_images_returns_none_for_absent_or_malformed[None] PASSED [  1%]
tests/test_push_images.py::test_item_images_returns_none_for_absent_or_malformed[metadata1] PASSED [  1%]
tests/test_push_images.py::test_item_images_returns_none_for_absent_or_malformed[metadata2] PASSED [  2%]
tests/test_push_images.py::test_item_images_returns_none_for_absent_or_malformed[metadata3] PASSED [  2%]
tests/test_push_images.py::test_item_images_returns_none_for_absent_or_malformed[metadata4] PASSED [  2%]
tests/test_push_images.py::test_item_images_non_string_caption_degrades_to_empty PASSED [  3%]
tests/test_push_images.py::test_build_photo_caption_title_only_without_excerpt PASSED [  3%]
tests/test_push_images.py::test_build_photo_caption_combines_title_and_excerpt_when_fits PASSED [  3%]
tests/test_push_images.py::test_build_photo_caption_clips_excerpt_to_caption_limit PASSED [  3%]
tests/test_push_images.py::test_build_photo_caption_oversized_title_keeps_title_only PASSED [  4%]
tests/test_push_images.py::test_telegram_immediate_sends_photo_first_then_message PASSED [  4%]
tests/test_push_images.py::test_telegram_immediate_via_send_immediate_dispatch PASSED [  4%]
tests/test_push_images.py::test_telegram_immediate_missing_image_falls_back_to_text_only PASSED [  5%]
tests/test_push_images.py::test_telegram_photo_api_failure_still_sends_text_message PASSED [  5%]
tests/test_push_images.py::test_telegram_digest_with_images_never_sends_photo PASSED [  5%]
tests/test_push_images.py::test_telegram_immediate_template_path_also_carries_photo PASSED [  6%]
tests/test_push_images.py::test_telegram_send_photo_unreadable_file_raises_structured_error PASSED [  6%]
tests/test_push_images.py::test_feishu_immediate_uploads_image_and_adds_img_element PASSED [  6%]
tests/test_push_images.py::test_feishu_immediate_template_path_also_gets_img_element PASSED [  6%]
tests/test_push_images.py::test_feishu_upload_failure_degrades_to_caption_summary_card PASSED [  7%]
tests/test_push_images.py::test_feishu_missing_image_files_skip_upload_and_use_summary_card PASSED [  7%]
tests/test_push_images.py::test_feishu_long_caption_excerpt_is_clipped_in_card PASSED [  7%]
tests/test_push_images.py::test_feishu_digest_with_images_card_is_byte_identical PASSED [  8%]
tests/test_push.py::test_resolve_route_score_ge_8_returns_immediate PASSED [  8%]
tests/test_push.py::test_resolve_route_score_between_5_and_8_returns_digest PASSED [  8%]
tests/test_push.py::test_resolve_route_score_below_5_returns_archive PASSED [  9%]
tests/test_push.py::test_resolve_route_reads_object_attributes PASSED    [  9%]
tests/test_push.py::test_route_buckets_items_into_three_modes PASSED     [  9%]
tests/test_push.py::test_resolve_route_without_score_immediate_categories_map_immediate[freebie] PASSED [ 10%]
tests/test_push.py::test_resolve_route_without_score_immediate_categories_map_immediate[proxy-node] PASSED [ 10%]
tests/test_push.py::test_resolve_route_without_score_immediate_categories_map_immediate[buying-agent] PASSED [ 10%]
tests/test_push.py::test_resolve_route_without_score_digest_categories_map_digest[ai-news] PASSED [ 10%]
tests/test_push.py::test_resolve_route_without_score_digest_categories_map_digest[server] PASSED [ 11%]
tests/test_push.py::test_resolve_route_without_score_digest_categories_map_digest[token] PASSED [ 11%]
tests/test_push.py::test_resolve_route_without_score_digest_categories_map_digest[credit-card] PASSED [ 11%]
tests/test_push.py::test_resolve_route_without_score_digest_categories_map_digest[channel] PASSED [ 12%]
tests/test_push.py::test_resolve_route_without_score_unknown_category_defaults_conservative_digest PASSED [ 12%]
tests/test_push.py::test_resolve_route_without_score_missing_category_defaults_conservative_digest PASSED [ 12%]
tests/test_push.py::test_category_default_mapping_covers_all_known_categories PASSED [ 13%]
tests/test_push.py::test_resolve_route_category_override_rule_beats_default_mapping PASSED [ 13%]
tests/test_push.py::test_resolve_route_score_rule_takes_precedence_over_category_mapping PASSED [ 13%]
tests/test_push.py::test_resolve_route_score_rules_stay_dormant_without_score PASSED [ 13%]
tests/test_push.py::test_resolve_route_with_score_and_no_rules_defaults_immediate PASSED [ 14%]
tests/test_push.py::test_resolve_route_with_score_and_unmatched_rules_defaults_conservative_digest PASSED [ 14%]
tests/test_push.py::test_route_rule_invalid_mode_raises_route_config_error PASSED [ 14%]
tests/test_push.py::test_route_rule_disallowed_expression_raises_route_config_error PASSED [ 15%]
tests/test_push.py::test_routes_from_config_accepts_raw_mappings_and_rule_objects PASSED [ 15%]
tests/test_push.py::test_routes_from_config_unknown_field_fails_fast PASSED [ 15%]
tests/test_push.py::test_routes_from_stocks_fixture_route_three_thresholds PASSED [ 16%]
tests/test_push.py::test_renderer_stocks_example_template_snapshot PASSED [ 16%]
tests/test_push.py::test_renderer_sandbox_blocks_dunder_attribute_access PASSED [ 16%]
tests/test_push.py::test_renderer_undefined_variable_raises_template_render_error PASSED [ 17%]
tests/test_push.py::test_renderer_exposes_date_slot_count_category_context PASSED [ 17%]
tests/test_push.py::test_build_card_aligns_production_card_json_shape PASSED [ 17%]
tests/test_push.py::test_feishu_send_posts_interactive_card_with_expected_body PASSED [ 17%]
tests/test_push.py::test_feishu_send_resolves_bot_token_from_env PASSED  [ 18%]
tests/test_push.py::test_feishu_send_template_renders_into_single_markdown_div PASSED [ 18%]
tests/test_push.py::test_feishu_send_api_error_raises_push_send_error PASSED [ 18%]
tests/test_push.py::test_feishu_send_http_failure_wraps_original_error PASSED [ 19%]
tests/test_push.py::test_feishu_send_missing_env_token_raises_structured_error PASSED [ 19%]
tests/test_push.py::test_feishu_send_noncanonical_keychain_target_raises_structured_error PASSED [ 19%]
tests/test_push.py::test_stdout_channel_prints_structured_json_line PASSED [ 20%]
tests/test_push.py::test_stdout_channel_with_template_includes_rendered_text PASSED [ 20%]
tests/test_push.py::test_digest_flush_merges_pool_into_one_card_per_channel PASSED [ 20%]
tests/test_push.py::test_digest_flush_suppresses_same_slot_repush_shared_registry PASSED [ 20%]
tests/test_push.py::test_digest_flush_releases_across_slots_pm_after_am PASSED [ 21%]
tests/test_push.py::test_digest_flush_records_push_slot_in_registry PASSED [ 21%]
tests/test_push.py::test_digest_flush_empty_pool_sends_nothing PASSED    [ 21%]
tests/test_push.py::test_digest_flush_partial_failure_reports_and_clears_pool PASSED [ 22%]
tests/test_push.py::test_digest_flush_all_channels_failed_keeps_pool_for_retry PASSED [ 22%]
tests/test_push.py::test_digest_slot_boundary_follows_local_noon PASSED  [ 22%]
tests/test_push.py::test_send_immediate_sends_each_item_and_records_push PASSED [ 23%]
tests/test_push.py::test_send_immediate_continues_after_channel_failure PASSED [ 23%]
tests/test_push.py::test_send_immediate_skips_same_slot_repush PASSED    [ 23%]
tests/test_push.py::test_item_view_merges_metadata_without_overriding_top_level PASSED [ 24%]
tests/test_push.py::test_send_context_rejects_unknown_slot_or_kind PASSED [ 24%]
tests/test_push.py::test_feishu_and_stdout_channels_conform_to_channel_protocol PASSED [ 24%]
tests/test_push.py::test_channel_send_failures_are_structured_reports[channel0] PASSED [ 24%]
tests/test_push.py::test_channel_send_failures_are_structured_reports[channel1] PASSED [ 25%]
tests/test_push.py::test_feishu_template_render_error_wrapped_as_push_send_error PASSED [ 25%]
tests/test_push.py::test_stdout_template_render_error_wrapped_as_push_send_error PASSED [ 25%]
tests/test_push.py::test_muted_item_without_rules_defaults_to_digest PASSED [ 26%]
tests/test_push.py::test_unmuted_zero_score_without_rules_still_immediate PASSED [ 26%]
tests/test_push.py::test_muted_item_with_score_rule_still_archives PASSED [ 26%]
tests/test_push_channels.py::test_split_message_empty_text_returns_empty_list PASSED [ 27%]
tests/test_push_channels.py::test_split_message_short_text_single_part PASSED [ 27%]
tests/test_push_channels.py::test_split_message_chunks_respect_limit_and_keep_order PASSED [ 27%]
tests/test_push_channels.py::test_split_message_hard_splits_oversized_single_line PASSED [ 27%]
tests/test_push_channels.py::test_telegram_send_posts_builtin_html_layout PASSED [ 28%]
tests/test_push_channels.py::test_telegram_send_escapes_html_in_title_and_url PASSED [ 28%]
tests/test_push_channels.py::test_telegram_send_auto_splits_message_over_4096_chars PASSED [ 28%]
tests/test_push_channels.py::test_telegram_send_template_sends_plain_text_without_parse_mode PASSED [ 29%]
tests/test_push_channels.py::test_telegram_send_template_render_error_wrapped_as_push_send_error PASSED [ 29%]
tests/test_push_channels.py::test_telegram_send_api_error_raises_telegram_api_error PASSED [ 29%]
tests/test_push_channels.py::test_telegram_send_non_json_response_raises_invalid_response PASSED [ 30%]
tests/test_push_channels.py::test_telegram_send_transport_error_wraps_cause_as_http_error PASSED [ 30%]
tests/test_push_channels.py::test_telegram_send_resolves_token_and_chat_from_default_env PASSED [ 30%]
tests/test_push_channels.py::test_telegram_send_missing_token_env_raises_env_var_missing PASSED [ 31%]
tests/test_push_channels.py::test_telegram_send_noncanonical_keychain_target_raises_structured_error PASSED [ 31%]
tests/test_push_channels.py::test_webhook_send_posts_payload_with_render_data_and_item_metadata PASSED [ 31%]
tests/test_push_channels.py::test_webhook_send_resolves_endpoint_from_default_env PASSED [ 31%]
tests/test_push_channels.py::test_webhook_send_plaintext_endpoint_refused PASSED [ 32%]
tests/test_push_channels.py::test_webhook_send_noncanonical_keychain_endpoint_raises_structured_error PASSED [ 32%]
tests/test_push_channels.py::test_webhook_send_template_includes_rendered_text_in_payload PASSED [ 32%]
tests/test_push_channels.py::test_webhook_send_template_render_error_wrapped_as_push_send_error PASSED [ 33%]
tests/test_push_channels.py::test_build_payload_omits_text_without_template PASSED [ 33%]
tests/test_push_channels.py::test_webhook_send_retries_transient_503_then_succeeds PASSED [ 33%]
tests/test_push_channels.py::test_webhook_send_exhausts_retries_on_permanent_500 PASSED [ 34%]
tests/test_push_channels.py::test_webhook_send_client_error_400_fails_without_retry PASSED [ 34%]
tests/test_push_channels.py::test_webhook_send_transport_error_retries_then_raises_http_error PASSED [ 34%]
tests/test_push_channels.py::test_webhook_send_timeout_exception_retries_then_raises_http_error PASSED [ 34%]
tests/test_push_channels.py::test_webhook_send_zero_retries_single_attempt PASSED [ 35%]
tests/test_push_channels.py::test_webhook_channel_rejects_negative_retry_config PASSED [ 35%]
tests/test_push_channels.py::test_route_digest_and_slot_suppression_flow_through_new_channels PASSED [ 35%]
tests/test_push_channels.py::test_new_channels_conform_to_channel_protocol_and_registry PASSED [ 36%]
tests/test_push_channels.py::test_item_line_long_title_never_cuts_tag PASSED [ 36%]
tests/test_push_channels.py::test_item_line_long_url_degrades_to_plain_text PASSED [ 36%]
tests/test_push_channels.py::test_item_line_truncated_entity_is_trimmed PASSED [ 37%]
tests/test_push_channels.py::test_item_also_line_truncation_reserves_full_suffix_width PASSED [ 37%]
tests/test_push_channels.py::test_item_also_line_truncation_keeps_refs_within_cap PASSED [ 37%]
tests/test_push_delivery.py::TestClassifyDeadError::test_forbidden_family_is_hard[\u98de\u4e66 API \u8fd4\u56de\u9519\u8bef: code=403 msg=forbidden: bot was blocked by the user] PASSED [ 37%]
tests/test_push_delivery.py::TestClassifyDeadError::test_forbidden_family_is_hard[telegram API \u8fd4\u56de\u9519\u8bef: error_code=403 description=Forbidden: bot was blocked] PASSED [ 38%]
tests/test_push_delivery.py::TestClassifyDeadError::test_forbidden_family_is_hard[not enough rights to send text messages to the chat] PASSED [ 38%]
tests/test_push_delivery.py::TestClassifyDeadError::test_forbidden_family_is_hard[user is deactivated] PASSED [ 38%]
tests/test_push_delivery.py::TestClassifyDeadError::test_chat_level_not_found_is_hard PASSED [ 39%]
tests/test_push_delivery.py::TestClassifyDeadError::test_thread_level_not_found_is_not_dead PASSED [ 39%]
tests/test_push_delivery.py::TestClassifyDeadError::test_transient_errors_never_mark_dead[telegram \u8bf7\u6c42\u5931\u8d25: ReadTimeout: timed out] PASSED [ 39%]
tests/test_push_delivery.py::TestClassifyDeadError::test_transient_errors_never_mark_dead[ConnectError: connection refused] PASSED [ 40%]
tests/test_push_delivery.py::TestClassifyDeadError::test_transient_errors_never_mark_dead[Too Many Requests: retry after 30] PASSED [ 40%]
tests/test_push_delivery.py::TestClassifyDeadError::test_transient_errors_never_mark_dead[\u98de\u4e66 API \u8fd4\u56de\u9519\u8bef: code=11232 msg=too many requests] PASSED [ 40%]
tests/test_push_delivery.py::TestClassifyDeadError::test_push_send_error_blob_includes_code_and_message PASSED [ 41%]
tests/test_push_delivery.py::TestDeliveryLedger::test_mark_and_reload_persists PASSED [ 41%]
tests/test_push_delivery.py::TestDeliveryLedger::test_remark_is_idempotent PASSED [ 41%]
tests/test_push_delivery.py::TestDeliveryLedger::test_clear_heals_and_persists PASSED [ 41%]
tests/test_push_delivery.py::TestDeliveryLedger::test_corrupt_file_degrades_to_empty PASSED [ 42%]
tests/test_push_delivery.py::TestDeliveryLedger::test_malformed_entries_dropped_on_load PASSED [ 42%]
tests/test_push_delivery.py::TestDeliveryLedger::test_unwritable_root_keeps_memory_state PASSED [ 42%]
tests/test_push_delivery.py::TestDeliveryLedger::test_platform_case_and_space_normalized PASSED [ 43%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_each_target_gets_its_own_card_with_context_target PASSED [ 43%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_empty_specs_short_circuit PASSED [ 43%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_single_failure_isolated_per_target PASSED [ 44%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_forbidden_marks_dead_after_single_failure_no_threshold PASSED [ 44%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_transient_failure_never_marks_dead PASSED [ 44%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_chat_level_not_found_marks_dead_thread_level_does_not PASSED [ 44%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_success_heals_dead_mark PASSED [ 45%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_recovered_target_receives_again_after_heal PASSED [ 45%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_unresolved_spec_reports_skipped_with_candidates PASSED [ 45%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_unsupported_channel_fails_loud_no_misdelivery PASSED [ 46%]
tests/test_push_delivery.py::TestSendBatchToTargets::test_dead_skip_logs_structured_info_no_alert_card PASSED [ 46%]
tests/test_push_directory.py::TestEmptyState::test_fresh_root_is_empty_and_lazy_paths PASSED [ 46%]
tests/test_push_directory.py::TestEmptyState::test_missing_files_survive_save_roundtrip PASSED [ 47%]
tests/test_push_directory.py::TestAliasOverlay::test_alias_renames_discovered_entry_on_load PASSED [ 47%]
tests/test_push_directory.py::TestAliasOverlay::test_alias_for_undiscovered_id_creates_placeholder PASSED [ 47%]
tests/test_push_directory.py::TestAliasOverlay::test_alias_survives_rebuild_hermes_regression PASSED [ 48%]
tests/test_push_directory.py::TestAliasOverlay::test_set_alias_writes_overlay_and_empty_name_deletes PASSED [ 48%]
tests/test_push_directory.py::TestCorruptJsonTolerance::test_corrupt_directory_file_degrades_to_empty PASSED [ 48%]
tests/test_push_directory.py::TestCorruptJsonTolerance::test_non_dict_directory_file_degrades_to_empty PASSED [ 48%]
tests/test_push_directory.py::TestCorruptJsonTolerance::test_corrupt_alias_file_is_ignored PASSED [ 49%]
tests/test_push_directory.py::TestCorruptJsonTolerance::test_malformed_entries_are_dropped PASSED [ 49%]
tests/test_push_directory.py::TestAtomicWrite::test_save_leaves_no_tmp_file_and_is_utf8 PASSED [ 49%]
tests/test_push_directory.py::TestAtomicWrite::test_save_via_tmp_rename_keeps_old_content_on_write_failure PASSED [ 50%]
tests/test_push_directory.py::TestAtomicWrite::test_unwritable_root_degrades_to_memory_state PASSED [ 50%]
tests/test_push_directory.py::TestPlatformBucketReplace::test_replace_is_whole_bucket_not_merge PASSED [ 50%]
tests/test_push_directory.py::TestPlatformBucketReplace::test_replace_dedupes_same_chat_id_first_wins PASSED [ 51%]
tests/test_push_directory.py::TestPlatformBucketReplace::test_other_platform_bucket_untouched PASSED [ 51%]
tests/test_push_directory.py::TestPlatformBucketReplace::test_last_seen_defaults_to_refresh_stamp PASSED [ 51%]
tests/test_push_directory.py::TestRefresh::test_refresh_replaces_buckets_and_persists PASSED [ 51%]
tests/test_push_directory.py::TestRefresh::test_refresh_failure_keeps_old_bucket_and_other_platforms PASSED [ 52%]
tests/test_push_directory.py::TestRefresh::test_refresh_skips_adapters_without_discovery PASSED [ 52%]
tests/test_push_directory.py::TestRefresh::test_refresh_updated_at_drives_age PASSED [ 52%]
tests/test_push_targets.py::TestParseSpec::test_valid_spec PASSED        [ 53%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[] PASSED [ 53%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[feishu] PASSED [ 53%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[feishu:] PASSED [ 54%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[\u7fa4\u540d] PASSED [ 54%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[Feishu:x] PASSED [ 54%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[fei-shu:x] PASSED [ 55%]
tests/test_push_targets.py::TestParseSpec::test_bad_format_rejected[:x] PASSED [ 55%]
tests/test_push_targets.py::TestDirectRefHook::test_explicit_feishu_id_bypasses_directory PASSED [ 55%]
tests/test_push_targets.py::TestDirectRefHook::test_hook_miss_falls_through_to_directory PASSED [ 55%]
tests/test_push_targets.py::TestDirectRefHook::test_no_registry_still_resolves_directory PASSED [ 56%]
tests/test_push_targets.py::TestDirectRefHook::test_hook_returning_bare_tuple_is_upgraded PASSED [ 56%]
tests/test_push_targets.py::TestDirectoryResolution::test_exact_chat_id_case_sensitive PASSED [ 56%]
tests/test_push_targets.py::TestDirectoryResolution::test_exact_name_case_insensitive PASSED [ 57%]
tests/test_push_targets.py::TestDirectoryResolution::test_exact_name_with_hash_prefix_normalized PASSED [ 57%]
tests/test_push_targets.py::TestDirectoryResolution::test_unique_prefix PASSED [ 57%]
tests/test_push_targets.py::TestDirectoryResolution::test_prefix_across_case_and_ascii PASSED [ 58%]
tests/test_push_targets.py::TestResolveErrors::test_ambiguous_prefix_lists_candidates PASSED [ 58%]
tests/test_push_targets.py::TestResolveErrors::test_miss_embeds_candidates PASSED [ 58%]
tests/test_push_targets.py::TestResolveErrors::test_empty_platform_bucket_reports_unknown_platform PASSED [ 58%]
tests/test_push_targets.py::TestResolveErrors::test_target_key_shape PASSED [ 59%]
tests/test_push_targets.py::TestResolveAll::test_partial_failure_returns_targets_and_errors PASSED [ 59%]
tests/test_push_targets.py::TestResolveAll::test_empty_specs PASSED      [ 59%]
tests/test_push_targets.py::TestSafety::test_no_eval_dangerous_looking_names_just_fail PASSED [ 60%]
tests/test_push_schema_targets.py::TestChannelPlatformMap::test_builtin_literal_map PASSED [ 60%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_valid_targets_load PASSED [ 60%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[] PASSED [ 61%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[   ] PASSED [ 61%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[feishu] PASSED [ 61%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[feishu:] PASSED [ 62%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[:x] PASSED [ 62%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[Feishu:\u7fa4] PASSED [ 62%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_bad_element_rejected[fei-shu:\u7fa4] PASSED [ 62%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_non_string_element_rejected_by_type_guard PASSED [ 63%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_duplicates_deduped_preserving_order PASSED [ 63%]
tests/test_push_schema_targets.py::TestTargetsFormat::test_rule_targets_format_checked PASSED [ 63%]
tests/test_push_schema_targets.py::TestSamePlatformConstraint::test_cross_platform_element_rejected PASSED [ 64%]
tests/test_push_schema_targets.py::TestSamePlatformConstraint::test_rule_targets_cross_platform_rejected PASSED [ 64%]
tests/test_push_schema_targets.py::TestSamePlatformConstraint::test_same_platform_rule_targets_accepted PASSED [ 64%]
tests/test_push_schema_targets.py::TestUnsupportedChannels::test_webhook_targets_rejected PASSED [ 65%]
tests/test_push_schema_targets.py::TestUnsupportedChannels::test_stdout_targets_rejected PASSED [ 65%]
tests/test_push_schema_targets.py::TestUnsupportedChannels::test_stdout_rule_targets_rejected PASSED [ 65%]
tests/test_push_schema_targets.py::TestUnsupportedChannels::test_webhook_rule_targets_rejected PASSED [ 65%]
tests/test_push_schema_targets.py::TestMissingTargetRelaxation::test_targets_only_entry_without_target_accepted PASSED [ 66%]
tests/test_push_schema_targets.py::TestMissingTargetRelaxation::test_neither_target_nor_targets_rejected PASSED [ 66%]
tests/test_push_schema_targets.py::TestMissingTargetRelaxation::test_legacy_target_alone_still_valid PASSED [ 66%]
tests/test_push_schema_targets.py::TestGoldenRegression::test_legacy_yaml_load_byte_identical_to_before PASSED [ 67%]
tests/test_push_schema_targets.py::TestW2PlatformChannels::test_w2_channels_accept_targets_and_optional_fields PASSED [ 67%]
tests/test_push_schema_targets.py::TestW2PlatformChannels::test_w2_same_platform_constraint PASSED [ 67%]
tests/test_push_schema_targets.py::TestW2PlatformChannels::test_w2_credential_field_on_wrong_channel_rejected PASSED [ 68%]
tests/test_push_schema_targets.py::TestW2PlatformChannels::test_w2_credential_field_plaintext_rejected PASSED [ 68%]
tests/test_push_schema_targets.py::TestW2PlatformChannels::test_w2_legacy_only_config_unchanged PASSED [ 68%]
tests/test_push_schema_targets.py::TestWeixinBridgeChannels::test_push_channel_vocabulary_accepts_weixin PASSED [ 68%]
tests/test_push_schema_targets.py::TestWeixinBridgeChannels::test_weixin_targets_and_hermes_bin_load PASSED [ 69%]
tests/test_push_schema_targets.py::TestWeixinBridgeChannels::test_weixin_same_platform_constraint PASSED [ 69%]
tests/test_push_schema_targets.py::TestWeixinBridgeChannels::test_hermes_bin_on_wrong_channel_rejected PASSED [ 69%]
tests/test_push_schema_targets.py::TestWeixinBridgeChannels::test_weixin_legacy_target_ref_still_valid PASSED [ 70%]
tests/test_feishu_targeting.py::TestTargetedSend::test_supports_targeting_is_true PASSED [ 70%]
tests/test_feishu_targeting.py::TestTargetedSend::test_context_target_overrides_channel_legacy_target PASSED [ 70%]
tests/test_feishu_targeting.py::TestTargetedSend::test_legacy_target_used_when_no_context_target PASSED [ 71%]
tests/test_feishu_targeting.py::TestTargetedSend::test_targets_only_config_sends_via_context_target PASSED [ 71%]
tests/test_feishu_targeting.py::TestTargetedSend::test_no_target_no_context_raises_missing_target PASSED [ 71%]
tests/test_feishu_targeting.py::TestTargetedSend::test_ou_prefix_routes_as_open_id PASSED [ 72%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_id_prefixes_resolve_direct[oc_9a79abc123-oc_9a79abc123-None] PASSED [ 72%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_id_prefixes_resolve_direct[ou_2f4c5d6e-ou_2f4c5d6e-None] PASSED [ 72%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_id_prefixes_resolve_direct[on_union0id-on_union0id-None] PASSED [ 72%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_id_prefixes_resolve_direct[chat_legacy99-chat_legacy99-None] PASSED [ 73%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_id_prefixes_resolve_direct[open_scoped42-open_scoped42-None] PASSED [ 73%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_id_prefixes_resolve_direct[oc_group1:mt_topic2-oc_group1-mt_topic2] PASSED [ 73%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_non_id_refs_return_none[AI\u4e2d\u8f6c\u7ad9\u5408\u4f19\u4eba\u7fa4] PASSED [ 74%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_non_id_refs_return_none[oc_] PASSED [ 74%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_non_id_refs_return_none[#\u7fa4\u540d] PASSED [ 74%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_non_id_refs_return_none[mt_topic_only] PASSED [ 75%]
tests/test_feishu_targeting.py::TestParseDirectRef::test_non_id_refs_return_none[oc_a:b:c] PASSED [ 75%]
tests/test_feishu_targeting.py::TestResolveTargetWiring::test_direct_ref_bypasses_directory PASSED [ 75%]
tests/test_feishu_targeting.py::TestResolveTargetWiring::test_name_resolution_still_reaches_directory PASSED [ 75%]
tests/test_feishu_discovery.py::TestPaginationAggregation::test_multi_page_aggregates_all_groups PASSED [ 76%]
tests/test_feishu_discovery.py::TestPaginationAggregation::test_page_size_capped_at_100_with_max_pages_guard PASSED [ 76%]
tests/test_feishu_discovery.py::TestPaginationAggregation::test_has_more_without_page_token_stops_defensively PASSED [ 76%]
tests/test_feishu_discovery.py::TestEntryMapping::test_entries_map_platform_group_type_with_no_thread PASSED [ 77%]
tests/test_feishu_discovery.py::TestEntryMapping::test_dissolved_groups_skipped_and_nameless_falls_back_to_chat_id PASSED [ 77%]
tests/test_feishu_discovery.py::TestEntryMapping::test_malformed_items_skipped_quietly PASSED [ 77%]
tests/test_feishu_discovery.py::TestEntryMapping::test_empty_directory_returns_empty_list PASSED [ 78%]
tests/test_feishu_discovery.py::TestRateLimitAndAuthErrors::test_429_backs_off_once_then_succeeds PASSED [ 78%]
tests/test_feishu_discovery.py::TestRateLimitAndAuthErrors::test_429_twice_raises_structured_error PASSED [ 78%]
tests/test_feishu_discovery.py::TestRateLimitAndAuthErrors::test_401_invalid_token_raises_structured_error PASSED [ 79%]
tests/test_feishu_discovery.py::TestRateLimitAndAuthErrors::test_missing_token_env_raises_credential_error PASSED [ 79%]
tests/test_feishu_discovery.py::TestRateLimitAndAuthErrors::test_non_json_response_raises_invalid_response PASSED [ 79%]
tests/test_feishu_discovery.py::TestRateLimitAndAuthErrors::test_missing_data_object_raises_invalid_response PASSED [ 79%]
tests/test_feishu_discovery.py::TestDirectoryMerge::test_discovered_entries_merge_into_channel_directory PASSED [ 80%]
tests/test_feishu_discovery.py::TestDirectoryMerge::test_discovery_failure_keeps_old_bucket_and_ledger_untouched PASSED [ 80%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_numeric_ids_go_direct[12345] PASSED [ 80%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_numeric_ids_go_direct[-1001234567890] PASSED [ 81%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_numeric_ids_go_direct[-777] PASSED [ 81%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_numeric_ids_go_direct[0] PASSED [ 81%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_public_username_goes_direct PASSED [ 82%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[\u5f20\u4e09] PASSED [ 82%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[abc] PASSED [ 82%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[@ab] PASSED [ 82%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[@bad name] PASSED [ 83%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[@x!y] PASSED [ 83%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[12 34] PASSED [ 83%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_non_matching_refs_fall_back_to_directory[] PASSED [ 84%]
tests/test_messaging_telegram.py::TestDirectRefParse::test_resolve_via_registry_bypasses_directory PASSED [ 84%]
tests/test_messaging_telegram.py::TestTargetedSend::test_context_target_overrides_legacy_chat_id PASSED [ 84%]
tests/test_messaging_telegram.py::TestTargetedSend::test_legacy_fallback_without_target_context PASSED [ 85%]
tests/test_messaging_telegram.py::TestTargetedSend::test_explicit_target_reference_still_resolves PASSED [ 85%]
tests/test_messaging_telegram.py::TestTargetedSend::test_username_not_found_error_hints_numeric_id PASSED [ 85%]
tests/test_messaging_telegram.py::TestTargetedSend::test_numeric_target_not_found_carries_no_hint PASSED [ 86%]
tests/test_messaging_telegram.py::TestTargetedSend::test_transient_error_on_username_target_has_no_hint PASSED [ 86%]
tests/test_messaging_telegram.py::TestTargetedSend::test_splitting_applies_to_every_targeted_chunk PASSED [ 86%]
tests/test_messaging_telegram.py::TestRegistration::test_platforms_registry_contains_telegram PASSED [ 86%]
tests/test_messaging_telegram.py::TestRegistration::test_registered_channel_supports_targeting_contract PASSED [ 87%]
tests/test_messaging_telegram.py::TestChatNormalization::test_group_and_supergroup_map_to_group_type PASSED [ 87%]
tests/test_messaging_telegram.py::TestChatNormalization::test_channel_maps_to_channel_type_with_title PASSED [ 87%]
tests/test_messaging_telegram.py::TestChatNormalization::test_private_dm_name_is_first_last_plus_username_note PASSED [ 88%]
tests/test_messaging_telegram.py::TestChatNormalization::test_private_without_username_keeps_plain_name PASSED [ 88%]
tests/test_messaging_telegram.py::TestChatNormalization::test_private_name_falls_back_to_username_then_chat_id PASSED [ 88%]
tests/test_messaging_telegram.py::TestChatNormalization::test_malformed_chat_is_dropped[None] PASSED [ 89%]
tests/test_messaging_telegram.py::TestChatNormalization::test_malformed_chat_is_dropped[chat] PASSED [ 89%]
tests/test_messaging_telegram.py::TestChatNormalization::test_malformed_chat_is_dropped[bad2] PASSED [ 89%]
tests/test_messaging_telegram.py::TestChatNormalization::test_malformed_chat_is_dropped[bad3] PASSED [ 89%]
tests/test_messaging_telegram.py::TestChatNormalization::test_malformed_chat_is_dropped[bad4] PASSED [ 90%]
tests/test_messaging_telegram.py::TestChatNormalization::test_effective_chat_from_message_and_callback_query PASSED [ 90%]
tests/test_messaging_telegram.py::TestChatNormalization::test_updates_without_chat_yield_none PASSED [ 90%]
tests/test_messaging_telegram.py::TestPollerSink::test_poll_dispatches_every_observed_chat PASSED [ 91%]
tests/test_messaging_telegram.py::TestPollerSink::test_sink_error_never_breaks_polling PASSED [ 91%]
tests/test_messaging_telegram.py::TestPollerSink::test_without_sink_behaviour_is_unchanged PASSED [ 91%]
tests/test_messaging_telegram.py::TestPollerSink::test_poll_error_path_skips_sink_entirely PASSED [ 92%]
tests/test_messaging_telegram.py::TestDirectoryMerge::test_sink_merges_into_directory_and_persists PASSED [ 92%]
tests/test_messaging_telegram.py::TestDirectoryMerge::test_merge_refreshes_existing_and_alias_still_wins PASSED [ 92%]
tests/test_messaging_telegram.py::TestPipelineWiring::test_poller_build_injects_directory_sink PASSED [ 93%]
tests/test_messaging_telegram.py::TestPipelineWiring::test_poller_absent_without_telegram_channel PASSED [ 93%]
tests/test_messaging_telegram.py::TestPipelineWiring::test_poller_absent_when_token_unresolvable PASSED [ 93%]
tests/test_messaging_telegram.py::TestChannelsCliPassivePlatform::test_refresh_reports_passive_not_failure PASSED [ 93%]
tests/test_messaging_telegram.py::TestChannelsCliPassivePlatform::test_refresh_default_all_platforms_isolates_passive PASSED [ 94%]
tests/test_messaging_pipeline.py::TestLegacyPathUnchanged::test_no_targets_sends_without_target_context PASSED [ 94%]
tests/test_messaging_pipeline.py::TestImmediateTargeted::test_channel_level_targets_dispatch_per_target PASSED [ 94%]
tests/test_messaging_pipeline.py::TestImmediateTargeted::test_rule_targets_override_channel_level PASSED [ 95%]
tests/test_messaging_pipeline.py::TestImmediateTargeted::test_unsupported_channel_fails_loud_core_state PASSED [ 95%]
tests/test_messaging_pipeline.py::TestDigestTargeted::test_digest_one_card_per_target PASSED [ 95%]
tests/test_messaging_pipeline.py::TestDigestTargeted::test_mixed_rule_targets_split_cards PASSED [ 96%]
tests/test_messaging_pipeline.py::TestLazyDirectoryRefresh::test_stale_directory_triggers_discovery PASSED [ 96%]
tests/test_messaging_pipeline.py::TestLazyDirectoryRefresh::test_fresh_directory_skips_discovery PASSED [ 96%]
tests/test_messaging_pipeline.py::TestLazyDirectoryRefresh::test_discovery_failure_falls_back_and_pushes PASSED [ 96%]
tests/test_messaging_pipeline.py::TestLazyDirectoryRefresh::test_dry_run_never_discovers PASSED [ 97%]
tests/test_messaging_pipeline.py::TestLazyDirectoryRefresh::test_empty_platforms_registry_zero_overhead PASSED [ 97%]
tests/test_messaging_pipeline.py::TestDigestPoolingSemantics::test_all_failed_stays_pooled_legacy PASSED [ 97%]
tests/test_messaging_pipeline.py::TestDigestPoolingSemantics::test_all_terminal_skipped_drops_not_repools PASSED [ 98%]
tests/test_messaging_pipeline.py::TestDeadLedgerWiring::test_forbidden_via_pipeline_lands_in_ledger_file PASSED [ 98%]
tests/test_messaging_pipeline.py::TestW2PipelineWiring::test_build_channel_passes_w2_credential_fields PASSED [ 98%]
tests/test_messaging_pipeline.py::TestW2PipelineWiring::test_legacy_w2_entries_stay_bare PASSED [ 99%]
tests/test_messaging_pipeline.py::TestW2PipelineWiring::test_stale_refresh_skips_no_discovery_platforms PASSED [ 99%]
tests/test_messaging_pipeline.py::TestWeixinBridgePipelineWiring::test_build_channel_passes_hermes_bin PASSED [ 99%]
tests/test_messaging_pipeline.py::TestWeixinBridgePipelineWiring::test_bare_weixin_entry_keeps_default_bin PASSED [100%]

============================= 329 passed in 0.49s ==============================
