infinity-context 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1053) hide show
  1. infinity_context-0.1.0.dist-info/METADATA +239 -0
  2. infinity_context-0.1.0.dist-info/RECORD +1053 -0
  3. infinity_context-0.1.0.dist-info/WHEEL +5 -0
  4. infinity_context-0.1.0.dist-info/entry_points.txt +9 -0
  5. infinity_context-0.1.0.dist-info/licenses/LICENSE +201 -0
  6. infinity_context-0.1.0.dist-info/top_level.txt +8 -0
  7. infinity_context_adapters/__init__.py +1 -0
  8. infinity_context_adapters/cognee/__init__.py +5 -0
  9. infinity_context_adapters/cognee/adapter.py +364 -0
  10. infinity_context_adapters/embeddings/__init__.py +6 -0
  11. infinity_context_adapters/embeddings/noop_adapter.py +5 -0
  12. infinity_context_adapters/embeddings/openai_adapter.py +104 -0
  13. infinity_context_adapters/extraction/__init__.py +41 -0
  14. infinity_context_adapters/extraction/content.py +828 -0
  15. infinity_context_adapters/extraction/docling_engine.py +255 -0
  16. infinity_context_adapters/extraction/document_normalization.py +434 -0
  17. infinity_context_adapters/extraction/factory.py +69 -0
  18. infinity_context_adapters/extraction/fallback_context.py +137 -0
  19. infinity_context_adapters/extraction/file_detection.py +378 -0
  20. infinity_context_adapters/extraction/image_evidence.py +319 -0
  21. infinity_context_adapters/extraction/media_tools.py +579 -0
  22. infinity_context_adapters/extraction/openai_json.py +356 -0
  23. infinity_context_adapters/extraction/openai_vision.py +722 -0
  24. infinity_context_adapters/extraction/prompts/__init__.py +1 -0
  25. infinity_context_adapters/extraction/prompts/semantic_memory_v1.md +20 -0
  26. infinity_context_adapters/extraction/schemas/__init__.py +1 -0
  27. infinity_context_adapters/extraction/schemas/semantic_memory_v1.json +62 -0
  28. infinity_context_adapters/extraction/transcription/__init__.py +11 -0
  29. infinity_context_adapters/extraction/transcription/openai_adapter.py +400 -0
  30. infinity_context_adapters/extraction/transcription_engine.py +544 -0
  31. infinity_context_adapters/extraction/video_evidence.py +129 -0
  32. infinity_context_adapters/extraction/whisper_engine.py +386 -0
  33. infinity_context_adapters/features/__init__.py +7 -0
  34. infinity_context_adapters/features/context_building/__init__.py +53 -0
  35. infinity_context_adapters/features/context_building/candidate_provider_chain.py +68 -0
  36. infinity_context_adapters/features/context_building/graphiti_candidate_provider.py +44 -0
  37. infinity_context_adapters/features/context_building/in_memory_candidate_provider.py +62 -0
  38. infinity_context_adapters/features/context_building/postgres_candidate_provider.py +44 -0
  39. infinity_context_adapters/features/context_building/qdrant_candidate_provider.py +44 -0
  40. infinity_context_adapters/features/context_building/query_request.py +99 -0
  41. infinity_context_adapters/features/context_building/records.py +113 -0
  42. infinity_context_adapters/features/document_ingestion/__init__.py +74 -0
  43. infinity_context_adapters/features/document_ingestion/composition.py +69 -0
  44. infinity_context_adapters/features/document_ingestion/extraction_adapter.py +114 -0
  45. infinity_context_adapters/features/document_ingestion/in_memory_chunk_index.py +67 -0
  46. infinity_context_adapters/features/document_ingestion/in_memory_document_store.py +176 -0
  47. infinity_context_adapters/features/document_ingestion/postgres_document_store.py +110 -0
  48. infinity_context_adapters/features/document_ingestion/qdrant_chunk_index.py +49 -0
  49. infinity_context_adapters/features/document_ingestion/records.py +88 -0
  50. infinity_context_adapters/features/memory_facts/__init__.py +53 -0
  51. infinity_context_adapters/features/memory_facts/graphiti_fact_projection.py +111 -0
  52. infinity_context_adapters/features/memory_facts/in_memory_fact_store.py +200 -0
  53. infinity_context_adapters/features/memory_facts/postgres_fact_store.py +126 -0
  54. infinity_context_adapters/features/memory_facts/qdrant_fact_projection.py +100 -0
  55. infinity_context_adapters/features/memory_scopes/__init__.py +44 -0
  56. infinity_context_adapters/features/memory_scopes/in_memory_scope_store.py +189 -0
  57. infinity_context_adapters/features/memory_scopes/postgres_scope_store.py +121 -0
  58. infinity_context_adapters/features/memory_scopes/records.py +86 -0
  59. infinity_context_adapters/graphiti/__init__.py +5 -0
  60. infinity_context_adapters/graphiti/adapter.py +613 -0
  61. infinity_context_adapters/graphiti/identity_evidence.py +711 -0
  62. infinity_context_adapters/graphiti/scope_identity.py +31 -0
  63. infinity_context_adapters/local_blob.py +101 -0
  64. infinity_context_adapters/noop/__init__.py +16 -0
  65. infinity_context_adapters/noop/adapters.py +156 -0
  66. infinity_context_adapters/noop/runtime.py +21 -0
  67. infinity_context_adapters/postgres/__init__.py +19 -0
  68. infinity_context_adapters/postgres/asset_mappers.py +348 -0
  69. infinity_context_adapters/postgres/asset_repositories.py +865 -0
  70. infinity_context_adapters/postgres/benchmark_projection_schema.py +241 -0
  71. infinity_context_adapters/postgres/benchmark_run_completion.py +645 -0
  72. infinity_context_adapters/postgres/benchmark_run_repositories.py +908 -0
  73. infinity_context_adapters/postgres/benchmark_writer_fence.py +139 -0
  74. infinity_context_adapters/postgres/canonical_keyword_trigram.py +31 -0
  75. infinity_context_adapters/postgres/canonical_retrieval_batching.py +388 -0
  76. infinity_context_adapters/postgres/fact_repositories.py +766 -0
  77. infinity_context_adapters/postgres/mappers.py +745 -0
  78. infinity_context_adapters/postgres/migrations/0001_core_facts.sql +95 -0
  79. infinity_context_adapters/postgres/migrations/0002_threads_documents_chunks.sql +97 -0
  80. infinity_context_adapters/postgres/migrations/0003_suggestions.sql +24 -0
  81. infinity_context_adapters/postgres/migrations/0004_service_tokens.sql +16 -0
  82. infinity_context_adapters/postgres/migrations/0005_data_classification_lite.sql +8 -0
  83. infinity_context_adapters/postgres/migrations/0006_service_token_usage_metadata.sql +5 -0
  84. infinity_context_adapters/postgres/migrations/0007_service_token_permissions.sql +2 -0
  85. infinity_context_adapters/postgres/migrations/0008_service_token_memory_scope.sql +2 -0
  86. infinity_context_adapters/postgres/migrations/0009_outbox_lifecycle_hardening.sql +15 -0
  87. infinity_context_adapters/postgres/migrations/0010_fact_taxonomy.sql +14 -0
  88. infinity_context_adapters/postgres/migrations/0011_asset_extractions.sql +66 -0
  89. infinity_context_adapters/postgres/migrations/0012_asset_extraction_execution_hardening.sql +20 -0
  90. infinity_context_adapters/postgres/migrations/0012_usage_governance.sql +33 -0
  91. infinity_context_adapters/postgres/migrations/0013_context_link_suggestions.sql +44 -0
  92. infinity_context_adapters/postgres/migrations/0014_memory_anchors.sql +26 -0
  93. infinity_context_adapters/postgres/migrations/0015_users_acl.sql +36 -0
  94. infinity_context_adapters/postgres/migrations/0016_managed_benchmark_runs.sql +27 -0
  95. infinity_context_adapters/postgres/migrations/0017_managed_benchmark_writer_fence.sql +104 -0
  96. infinity_context_adapters/postgres/migrations/0018_benchmark_projection_manifest.sql +51 -0
  97. infinity_context_adapters/postgres/migrations/0019_managed_benchmark_sealed_fence.sql +131 -0
  98. infinity_context_adapters/postgres/migrations/0020_benchmark_cleanup_completion.sql +219 -0
  99. infinity_context_adapters/postgres/migrations/0021_benchmark_unsealed_abort.sql +64 -0
  100. infinity_context_adapters/postgres/migrations/0022_canonical_keyword_trigram.sql +5 -0
  101. infinity_context_adapters/postgres/models.py +878 -0
  102. infinity_context_adapters/postgres/projection_fence.py +80 -0
  103. infinity_context_adapters/postgres/repositories.py +906 -0
  104. infinity_context_adapters/postgres/repository_helpers.py +115 -0
  105. infinity_context_adapters/postgres/scope_repositories.py +540 -0
  106. infinity_context_adapters/postgres/unit_of_work.py +785 -0
  107. infinity_context_adapters/postgres/usage_repositories.py +128 -0
  108. infinity_context_adapters/postgres/user_repositories.py +164 -0
  109. infinity_context_adapters/provider_errors.py +73 -0
  110. infinity_context_adapters/qdrant/__init__.py +5 -0
  111. infinity_context_adapters/qdrant/identity_evidence.py +504 -0
  112. infinity_context_adapters/qdrant/vector_adapter.py +718 -0
  113. infinity_context_adapters/s3_blob.py +242 -0
  114. infinity_context_cli/__init__.py +5 -0
  115. infinity_context_cli/__main__.py +6 -0
  116. infinity_context_cli/cli.py +964 -0
  117. infinity_context_cli/config.py +184 -0
  118. infinity_context_cli/doctor.py +397 -0
  119. infinity_context_cli/local_experience.py +250 -0
  120. infinity_context_cli/mcp_config.py +124 -0
  121. infinity_context_cli/onboarding.py +542 -0
  122. infinity_context_cli/runtime.py +179 -0
  123. infinity_context_contracts/__init__.py +16 -0
  124. infinity_context_contracts/_json.py +32 -0
  125. infinity_context_contracts/capabilities.py +67 -0
  126. infinity_context_contracts/common.py +54 -0
  127. infinity_context_contracts/features/__init__.py +12 -0
  128. infinity_context_contracts/features/context_building.py +248 -0
  129. infinity_context_contracts/features/document_ingestion.py +277 -0
  130. infinity_context_contracts/features/memory_facts.py +236 -0
  131. infinity_context_contracts/features/memory_scopes.py +329 -0
  132. infinity_context_contracts/health.py +23 -0
  133. infinity_context_core/__init__.py +76 -0
  134. infinity_context_core/agent_behavior_contract.py +72 -0
  135. infinity_context_core/application/__init__.py +580 -0
  136. infinity_context_core/application/anchor_creative_work_filters.py +148 -0
  137. infinity_context_core/application/anchor_event_extraction.py +1571 -0
  138. infinity_context_core/application/anchor_extraction.py +1144 -0
  139. infinity_context_core/application/anchor_identity_normalization.py +90 -0
  140. infinity_context_core/application/anchor_relation_projection.py +203 -0
  141. infinity_context_core/application/anchor_temporal_window.py +172 -0
  142. infinity_context_core/application/asset_extraction_mapping.py +236 -0
  143. infinity_context_core/application/asset_upload_policy.py +952 -0
  144. infinity_context_core/application/auto_apply.py +72 -0
  145. infinity_context_core/application/auto_memory.py +527 -0
  146. infinity_context_core/application/capture_policy.py +61 -0
  147. infinity_context_core/application/chunker.py +93 -0
  148. infinity_context_core/application/context_action_role_evidence.py +890 -0
  149. infinity_context_core/application/context_action_role_labels.py +176 -0
  150. infinity_context_core/application/context_action_roles.py +2365 -0
  151. infinity_context_core/application/context_action_subject_policy.py +71 -0
  152. infinity_context_core/application/context_activity_companion.py +120 -0
  153. infinity_context_core/application/context_activity_duration_rerank.py +118 -0
  154. infinity_context_core/application/context_activity_participant_grounding.py +87 -0
  155. infinity_context_core/application/context_aggregation_answer_slots.py +586 -0
  156. infinity_context_core/application/context_aggregation_evidence_text.py +409 -0
  157. infinity_context_core/application/context_aggregation_rerank.py +145 -0
  158. infinity_context_core/application/context_aggregation_subject.py +137 -0
  159. infinity_context_core/application/context_allergy_condition_inference.py +146 -0
  160. infinity_context_core/application/context_anchor_identity.py +157 -0
  161. infinity_context_core/application/context_anchor_intent_ranking.py +84 -0
  162. infinity_context_core/application/context_anchor_relations.py +67 -0
  163. infinity_context_core/application/context_anchors.py +393 -0
  164. infinity_context_core/application/context_answer_evidence_types.py +14 -0
  165. infinity_context_core/application/context_answer_unit_shapes.py +207 -0
  166. infinity_context_core/application/context_artifact_evidence.py +893 -0
  167. infinity_context_core/application/context_attribution_lexicon.py +254 -0
  168. infinity_context_core/application/context_awareness_cause_rerank.py +109 -0
  169. infinity_context_core/application/context_baby_birth_evidence.py +91 -0
  170. infinity_context_core/application/context_birdwatching_schedule_rerank.py +130 -0
  171. infinity_context_core/application/context_bm25_ranking.py +450 -0
  172. infinity_context_core/application/context_book_author_preference_exact_turns.py +110 -0
  173. infinity_context_core/application/context_building_legacy_candidate_adapter.py +530 -0
  174. infinity_context_core/application/context_canonical_fact_retrieval.py +409 -0
  175. infinity_context_core/application/context_causal_reason_rerank.py +145 -0
  176. infinity_context_core/application/context_choice_reason_rerank.py +109 -0
  177. infinity_context_core/application/context_collectors.py +931 -0
  178. infinity_context_core/application/context_commonality_rerank.py +428 -0
  179. infinity_context_core/application/context_community_membership_inference.py +139 -0
  180. infinity_context_core/application/context_competition_count_exact_turns.py +383 -0
  181. infinity_context_core/application/context_conflicting_evidence_rerank.py +282 -0
  182. infinity_context_core/application/context_conversation_counterparty.py +260 -0
  183. infinity_context_core/application/context_count_cardinality.py +164 -0
  184. infinity_context_core/application/context_creative_work_count_exact_turns.py +517 -0
  185. infinity_context_core/application/context_diagnostics.py +55 -0
  186. infinity_context_core/application/context_diagnostics_bundle_keys.py +433 -0
  187. infinity_context_core/application/context_diagnostics_bundle_projection.py +711 -0
  188. infinity_context_core/application/context_diagnostics_item_projection.py +410 -0
  189. infinity_context_core/application/context_diagnostics_primitives.py +131 -0
  190. infinity_context_core/application/context_diagnostics_ranking.py +143 -0
  191. infinity_context_core/application/context_diagnostics_signals.py +209 -0
  192. infinity_context_core/application/context_diagnostics_sources.py +82 -0
  193. infinity_context_core/application/context_dialogue_authority.py +382 -0
  194. infinity_context_core/application/context_distinct_set_evidence.py +994 -0
  195. infinity_context_core/application/context_distinct_set_selection.py +335 -0
  196. infinity_context_core/application/context_domain_rerank_apply.py +310 -0
  197. infinity_context_core/application/context_domain_rerank_signals.py +2391 -0
  198. infinity_context_core/application/context_domain_rerank_types.py +14 -0
  199. infinity_context_core/application/context_english_activity_displacement_answer_support.py +128 -0
  200. infinity_context_core/application/context_english_lifestyle_inference.py +122 -0
  201. infinity_context_core/application/context_english_lifestyle_inference_answer_support.py +291 -0
  202. infinity_context_core/application/context_english_temporal_dates.py +53 -0
  203. infinity_context_core/application/context_evidence_priority.py +262 -0
  204. infinity_context_core/application/context_evidence_reservation_safety.py +208 -0
  205. infinity_context_core/application/context_exercise_performance_rerank.py +71 -0
  206. infinity_context_core/application/context_food_inventory_exact_turns.py +583 -0
  207. infinity_context_core/application/context_frequency_rerank.py +109 -0
  208. infinity_context_core/application/context_future_plan_timing_rerank.py +110 -0
  209. infinity_context_core/application/context_generic_behavior_inference.py +348 -0
  210. infinity_context_core/application/context_geo_residence_aliases.py +149 -0
  211. infinity_context_core/application/context_hydration.py +329 -0
  212. infinity_context_core/application/context_identity_fact_rerank.py +362 -0
  213. infinity_context_core/application/context_identity_rerank.py +76 -0
  214. infinity_context_core/application/context_identity_terms.py +15 -0
  215. infinity_context_core/application/context_inference_evidence.py +484 -0
  216. infinity_context_core/application/context_inference_evidence_terms.py +744 -0
  217. infinity_context_core/application/context_internal_diagnostics.py +61 -0
  218. infinity_context_core/application/context_item_evidence.py +108 -0
  219. infinity_context_core/application/context_item_purchase_evidence.py +49 -0
  220. infinity_context_core/application/context_keyword_aggregation_prefilter.py +63 -0
  221. infinity_context_core/application/context_keyword_ranking.py +178 -0
  222. infinity_context_core/application/context_lexical.py +750 -0
  223. infinity_context_core/application/context_lifecycle_rerank.py +502 -0
  224. infinity_context_core/application/context_link_candidate_policy.py +864 -0
  225. infinity_context_core/application/context_link_expansion.py +727 -0
  226. infinity_context_core/application/context_link_expansion_items.py +441 -0
  227. infinity_context_core/application/context_link_policy.py +451 -0
  228. infinity_context_core/application/context_link_source_enrichment.py +120 -0
  229. infinity_context_core/application/context_media_time.py +368 -0
  230. infinity_context_core/application/context_museum_visit_policy.py +66 -0
  231. infinity_context_core/application/context_named_person_preference.py +313 -0
  232. infinity_context_core/application/context_national_park_inference.py +93 -0
  233. infinity_context_core/application/context_object_attribute_rerank.py +416 -0
  234. infinity_context_core/application/context_object_mismatch.py +63 -0
  235. infinity_context_core/application/context_occupational_role_identity.py +961 -0
  236. infinity_context_core/application/context_ordinal_sequence.py +134 -0
  237. infinity_context_core/application/context_owner_grounding.py +408 -0
  238. infinity_context_core/application/context_packer.py +858 -0
  239. infinity_context_core/application/context_packer_answer_slots.py +733 -0
  240. infinity_context_core/application/context_packer_answer_support.py +3101 -0
  241. infinity_context_core/application/context_packer_answer_support_patterns.py +1267 -0
  242. infinity_context_core/application/context_packer_answer_support_slots.py +1209 -0
  243. infinity_context_core/application/context_packer_answer_support_utils.py +473 -0
  244. infinity_context_core/application/context_packer_diagnostics.py +48 -0
  245. infinity_context_core/application/context_packer_exact_literal_turns.py +697 -0
  246. infinity_context_core/application/context_packer_exact_prepasses.py +758 -0
  247. infinity_context_core/application/context_packer_exact_turn_utils.py +133 -0
  248. infinity_context_core/application/context_packer_identity_policy.py +155 -0
  249. infinity_context_core/application/context_packer_inference_reservation.py +113 -0
  250. infinity_context_core/application/context_packer_inventory_prepasses.py +359 -0
  251. infinity_context_core/application/context_packer_inventory_slots.py +771 -0
  252. infinity_context_core/application/context_packer_marker_coverage.py +409 -0
  253. infinity_context_core/application/context_packer_rendering.py +338 -0
  254. infinity_context_core/application/context_packer_selection.py +487 -0
  255. infinity_context_core/application/context_packer_source_policy.py +123 -0
  256. infinity_context_core/application/context_paired_evidence_requirements.py +78 -0
  257. infinity_context_core/application/context_paired_evidence_roles.py +64 -0
  258. infinity_context_core/application/context_person_activity_exact_turns.py +375 -0
  259. infinity_context_core/application/context_person_aliases.py +49 -0
  260. infinity_context_core/application/context_person_kinship.py +296 -0
  261. infinity_context_core/application/context_person_occupation.py +240 -0
  262. infinity_context_core/application/context_person_relation_inventory.py +755 -0
  263. infinity_context_core/application/context_person_residence.py +398 -0
  264. infinity_context_core/application/context_person_team_membership.py +305 -0
  265. infinity_context_core/application/context_pet_ownership.py +223 -0
  266. infinity_context_core/application/context_polarity_rerank.py +338 -0
  267. infinity_context_core/application/context_policy.py +107 -0
  268. infinity_context_core/application/context_political_inference.py +128 -0
  269. infinity_context_core/application/context_possession_source.py +161 -0
  270. infinity_context_core/application/context_precise_temporal_evidence.py +156 -0
  271. infinity_context_core/application/context_preference_goal_rerank.py +428 -0
  272. infinity_context_core/application/context_preference_inference.py +335 -0
  273. infinity_context_core/application/context_property_interaction_evidence.py +659 -0
  274. infinity_context_core/application/context_provider_retrieval_terms.py +95 -0
  275. infinity_context_core/application/context_quality.py +488 -0
  276. infinity_context_core/application/context_quantity_evidence_patterns.py +254 -0
  277. infinity_context_core/application/context_quantity_evidence_slots.py +750 -0
  278. infinity_context_core/application/context_quantity_pending_clothing.py +200 -0
  279. infinity_context_core/application/context_query_artifact_inventory_expansions.py +76 -0
  280. infinity_context_core/application/context_query_decomposition.py +879 -0
  281. infinity_context_core/application/context_query_decomposition_answer_policy.py +707 -0
  282. infinity_context_core/application/context_query_decomposition_contracts.py +28 -0
  283. infinity_context_core/application/context_query_decomposition_event_policy.py +535 -0
  284. infinity_context_core/application/context_query_decomposition_inventory_policy.py +767 -0
  285. infinity_context_core/application/context_query_decomposition_shared.py +300 -0
  286. infinity_context_core/application/context_query_duration.py +171 -0
  287. infinity_context_core/application/context_query_entity_relation_expansions.py +212 -0
  288. infinity_context_core/application/context_query_event_summary_expansions.py +80 -0
  289. infinity_context_core/application/context_query_expansion.py +200 -0
  290. infinity_context_core/application/context_query_expansion_rule_catalog_part1.py +1279 -0
  291. infinity_context_core/application/context_query_expansion_rule_catalog_part2.py +770 -0
  292. infinity_context_core/application/context_query_expansion_rule_catalog_part3.py +982 -0
  293. infinity_context_core/application/context_query_expansion_rule_catalog_part4.py +1334 -0
  294. infinity_context_core/application/context_query_expansion_rule_catalog_part5.py +517 -0
  295. infinity_context_core/application/context_query_expansion_rule_terms.py +269 -0
  296. infinity_context_core/application/context_query_expansion_rules.py +66 -0
  297. infinity_context_core/application/context_query_expansion_selection.py +892 -0
  298. infinity_context_core/application/context_query_financial_resources_inference.py +92 -0
  299. infinity_context_core/application/context_query_food_expansions.py +89 -0
  300. infinity_context_core/application/context_query_frequency.py +194 -0
  301. infinity_context_core/application/context_query_game_expansions.py +141 -0
  302. infinity_context_core/application/context_query_health_lifestyle_expansions.py +178 -0
  303. infinity_context_core/application/context_query_identity_terms.py +81 -0
  304. infinity_context_core/application/context_query_intent.py +33 -0
  305. infinity_context_core/application/context_query_intent_common.py +29 -0
  306. infinity_context_core/application/context_query_intent_contracts.py +92 -0
  307. infinity_context_core/application/context_query_intent_extraction.py +614 -0
  308. infinity_context_core/application/context_query_intent_keys.py +499 -0
  309. infinity_context_core/application/context_query_intent_matching.py +407 -0
  310. infinity_context_core/application/context_query_organization_summary_expansions.py +75 -0
  311. infinity_context_core/application/context_query_personal_fact_expansions.py +314 -0
  312. infinity_context_core/application/context_query_pet_activity_expansions.py +329 -0
  313. infinity_context_core/application/context_query_preference_expansions.py +97 -0
  314. infinity_context_core/application/context_query_project_summary_expansions.py +60 -0
  315. infinity_context_core/application/context_query_relevance_ranking.py +76 -0
  316. infinity_context_core/application/context_query_sports_expansions.py +48 -0
  317. infinity_context_core/application/context_query_state_transition.py +53 -0
  318. infinity_context_core/application/context_query_support_role.py +165 -0
  319. infinity_context_core/application/context_query_temporal_expansions.py +18 -0
  320. infinity_context_core/application/context_query_travel_expansions.py +134 -0
  321. infinity_context_core/application/context_query_vehicle_expansions.py +79 -0
  322. infinity_context_core/application/context_query_workflow_expansions.py +233 -0
  323. infinity_context_core/application/context_query_workflow_intent.py +65 -0
  324. infinity_context_core/application/context_quote_scanner.py +342 -0
  325. infinity_context_core/application/context_rank_dedupe.py +171 -0
  326. infinity_context_core/application/context_rank_fusion.py +191 -0
  327. infinity_context_core/application/context_ranked_activity_reservation.py +586 -0
  328. infinity_context_core/application/context_ranked_evidence_coverage_reservation.py +427 -0
  329. infinity_context_core/application/context_ranked_evidence_priority_reservation.py +37 -0
  330. infinity_context_core/application/context_ranked_evidence_selection.py +260 -0
  331. infinity_context_core/application/context_ranking.py +3681 -0
  332. infinity_context_core/application/context_ranking_reason_policy.py +31 -0
  333. infinity_context_core/application/context_ranking_reason_priorities.py +859 -0
  334. infinity_context_core/application/context_ranking_reason_scores.py +519 -0
  335. infinity_context_core/application/context_recommendation_answer_support.py +358 -0
  336. infinity_context_core/application/context_recommendation_exact_turns.py +360 -0
  337. infinity_context_core/application/context_relation_requirement.py +498 -0
  338. infinity_context_core/application/context_relationship_status_evidence.py +83 -0
  339. infinity_context_core/application/context_relative_duration_evidence.py +61 -0
  340. infinity_context_core/application/context_relevance.py +430 -0
  341. infinity_context_core/application/context_reported_obligation_attribution.py +416 -0
  342. infinity_context_core/application/context_reporting_frames.py +338 -0
  343. infinity_context_core/application/context_requirement_coverage.py +767 -0
  344. infinity_context_core/application/context_requirement_guard.py +503 -0
  345. infinity_context_core/application/context_requirement_patterns.py +943 -0
  346. infinity_context_core/application/context_requirement_ranking.py +181 -0
  347. infinity_context_core/application/context_requirement_signals.py +101 -0
  348. infinity_context_core/application/context_rerank_domain_heuristics.py +433 -0
  349. infinity_context_core/application/context_rerank_evidence_signals.py +400 -0
  350. infinity_context_core/application/context_rerank_relevance.py +131 -0
  351. infinity_context_core/application/context_retrieval_performance.py +153 -0
  352. infinity_context_core/application/context_review_items.py +338 -0
  353. infinity_context_core/application/context_score_signal_rerank.py +21 -0
  354. infinity_context_core/application/context_short_story_progress_evidence.py +119 -0
  355. infinity_context_core/application/context_snippets.py +545 -0
  356. infinity_context_core/application/context_social_education_inference.py +265 -0
  357. infinity_context_core/application/context_social_inventory_rerank.py +329 -0
  358. infinity_context_core/application/context_source_family.py +100 -0
  359. infinity_context_core/application/context_source_grounding.py +277 -0
  360. infinity_context_core/application/context_source_sibling_answer_evidence_repair.py +1983 -0
  361. infinity_context_core/application/context_source_sibling_contracts.py +35 -0
  362. infinity_context_core/application/context_source_sibling_evidence.py +405 -0
  363. infinity_context_core/application/context_source_sibling_evidence_rules.py +798 -0
  364. infinity_context_core/application/context_source_sibling_evidence_shared.py +449 -0
  365. infinity_context_core/application/context_source_sibling_identity.py +114 -0
  366. infinity_context_core/application/context_source_sibling_inventory_evidence.py +28 -0
  367. infinity_context_core/application/context_source_sibling_patterns.py +946 -0
  368. infinity_context_core/application/context_source_sibling_place_evidence.py +561 -0
  369. infinity_context_core/application/context_source_sibling_policy.py +988 -0
  370. infinity_context_core/application/context_source_sibling_scoring.py +721 -0
  371. infinity_context_core/application/context_source_siblings.py +363 -0
  372. infinity_context_core/application/context_speaker_attribution.py +574 -0
  373. infinity_context_core/application/context_speaker_disambiguation.py +108 -0
  374. infinity_context_core/application/context_stage_diagnostics.py +111 -0
  375. infinity_context_core/application/context_state_evidence.py +196 -0
  376. infinity_context_core/application/context_state_residence_inference.py +163 -0
  377. infinity_context_core/application/context_state_transition_pairing.py +401 -0
  378. infinity_context_core/application/context_temporal_answer_grounding.py +187 -0
  379. infinity_context_core/application/context_temporal_hints.py +485 -0
  380. infinity_context_core/application/context_temporal_intent_policy.py +146 -0
  381. infinity_context_core/application/context_temporal_interval_requirements.py +107 -0
  382. infinity_context_core/application/context_temporal_metadata.py +29 -0
  383. infinity_context_core/application/context_temporal_query.py +1358 -0
  384. infinity_context_core/application/context_temporal_ranges.py +471 -0
  385. infinity_context_core/application/context_temporal_session_order.py +439 -0
  386. infinity_context_core/application/context_temporal_source_identity.py +426 -0
  387. infinity_context_core/application/context_temporal_source_turn.py +1290 -0
  388. infinity_context_core/application/context_temporal_source_turn_labels.py +265 -0
  389. infinity_context_core/application/context_temporal_source_turn_patterns.py +186 -0
  390. infinity_context_core/application/context_temporal_source_turn_types.py +62 -0
  391. infinity_context_core/application/context_travel_hobby_writing_evidence.py +176 -0
  392. infinity_context_core/application/context_travel_place_evidence.py +23 -0
  393. infinity_context_core/application/document_fragments.py +206 -0
  394. infinity_context_core/application/document_text.py +203 -0
  395. infinity_context_core/application/dto.py +311 -0
  396. infinity_context_core/application/dto_anchors.py +143 -0
  397. infinity_context_core/application/dto_assets.py +234 -0
  398. infinity_context_core/application/dto_benchmark_runs.py +114 -0
  399. infinity_context_core/application/dto_context.py +176 -0
  400. infinity_context_core/application/dto_context_links.py +170 -0
  401. infinity_context_core/application/dto_facts.py +275 -0
  402. infinity_context_core/application/dto_suggestions_capture.py +199 -0
  403. infinity_context_core/application/dto_workspace.py +101 -0
  404. infinity_context_core/application/extraction_coordinates.py +56 -0
  405. infinity_context_core/application/extraction_resource_policy.py +796 -0
  406. infinity_context_core/application/extractor.py +147 -0
  407. infinity_context_core/application/memory_digest_renderer.py +215 -0
  408. infinity_context_core/application/multimodal_manifest.py +418 -0
  409. infinity_context_core/application/normalize.py +29 -0
  410. infinity_context_core/application/observed_anchor_resolution.py +243 -0
  411. infinity_context_core/application/related_facts.py +144 -0
  412. infinity_context_core/application/review_payloads.py +104 -0
  413. infinity_context_core/application/safe_payload.py +86 -0
  414. infinity_context_core/application/semantic_dedupe.py +943 -0
  415. infinity_context_core/application/sensitive_text.py +39 -0
  416. infinity_context_core/application/source_refs.py +263 -0
  417. infinity_context_core/application/storage_key_safety.py +13 -0
  418. infinity_context_core/application/temporal_validity.py +31 -0
  419. infinity_context_core/application/use_cases/__init__.py +1 -0
  420. infinity_context_core/application/use_cases/anchors.py +1165 -0
  421. infinity_context_core/application/use_cases/asset_extraction_support.py +273 -0
  422. infinity_context_core/application/use_cases/asset_extractions.py +1454 -0
  423. infinity_context_core/application/use_cases/assets.py +463 -0
  424. infinity_context_core/application/use_cases/benchmark_runs.py +877 -0
  425. infinity_context_core/application/use_cases/benchmark_unsealed_abort.py +147 -0
  426. infinity_context_core/application/use_cases/blob_storage_cleanup.py +289 -0
  427. infinity_context_core/application/use_cases/blob_storage_integrity.py +351 -0
  428. infinity_context_core/application/use_cases/build_context.py +995 -0
  429. infinity_context_core/application/use_cases/build_context_final_packing.py +59 -0
  430. infinity_context_core/application/use_cases/build_context_item_projection.py +839 -0
  431. infinity_context_core/application/use_cases/build_context_keyword_aggregation.py +908 -0
  432. infinity_context_core/application/use_cases/build_context_keyword_aggregation_seeds.py +132 -0
  433. infinity_context_core/application/use_cases/build_context_keyword_aggregation_selection.py +280 -0
  434. infinity_context_core/application/use_cases/build_context_keyword_aggregation_support.py +161 -0
  435. infinity_context_core/application/use_cases/build_context_requirement_guard.py +156 -0
  436. infinity_context_core/application/use_cases/build_context_source_selection.py +935 -0
  437. infinity_context_core/application/use_cases/build_memory_digest.py +203 -0
  438. infinity_context_core/application/use_cases/build_memory_insights.py +979 -0
  439. infinity_context_core/application/use_cases/consolidate_capture.py +806 -0
  440. infinity_context_core/application/use_cases/context_link_crud.py +196 -0
  441. infinity_context_core/application/use_cases/context_link_reviews.py +441 -0
  442. infinity_context_core/application/use_cases/context_link_suggestions.py +1060 -0
  443. infinity_context_core/application/use_cases/context_link_visibility.py +106 -0
  444. infinity_context_core/application/use_cases/context_links.py +33 -0
  445. infinity_context_core/application/use_cases/delete_document.py +71 -0
  446. infinity_context_core/application/use_cases/delete_thread_memory.py +55 -0
  447. infinity_context_core/application/use_cases/duplicate_merge_resolution.py +307 -0
  448. infinity_context_core/application/use_cases/ensure_scope.py +29 -0
  449. infinity_context_core/application/use_cases/expire_suggestions.py +36 -0
  450. infinity_context_core/application/use_cases/export_graph.py +684 -0
  451. infinity_context_core/application/use_cases/fact_relations.py +204 -0
  452. infinity_context_core/application/use_cases/forget_fact.py +43 -0
  453. infinity_context_core/application/use_cases/get_capabilities.py +213 -0
  454. infinity_context_core/application/use_cases/get_capture.py +16 -0
  455. infinity_context_core/application/use_cases/get_session_status.py +23 -0
  456. infinity_context_core/application/use_cases/ingest_document.py +376 -0
  457. infinity_context_core/application/use_cases/ingest_episode.py +263 -0
  458. infinity_context_core/application/use_cases/list_captures.py +25 -0
  459. infinity_context_core/application/use_cases/memory_browser.py +414 -0
  460. infinity_context_core/application/use_cases/operations_console.py +293 -0
  461. infinity_context_core/application/use_cases/process_document.py +83 -0
  462. infinity_context_core/application/use_cases/purge_capture.py +34 -0
  463. infinity_context_core/application/use_cases/query_documents.py +42 -0
  464. infinity_context_core/application/use_cases/query_facts.py +57 -0
  465. infinity_context_core/application/use_cases/receive_capture.py +238 -0
  466. infinity_context_core/application/use_cases/related_facts.py +47 -0
  467. infinity_context_core/application/use_cases/remember_fact.py +145 -0
  468. infinity_context_core/application/use_cases/spaces_memory_scopes.py +134 -0
  469. infinity_context_core/application/use_cases/suggestions.py +804 -0
  470. infinity_context_core/application/use_cases/update_fact.py +43 -0
  471. infinity_context_core/application/use_cases/usage.py +49 -0
  472. infinity_context_core/application/use_cases/users.py +138 -0
  473. infinity_context_core/domain/__init__.py +1 -0
  474. infinity_context_core/domain/aggregation_admission.py +200 -0
  475. infinity_context_core/domain/anchor_entities.py +316 -0
  476. infinity_context_core/domain/assets.py +654 -0
  477. infinity_context_core/domain/capture.py +321 -0
  478. infinity_context_core/domain/distinct_set_membership.py +69 -0
  479. infinity_context_core/domain/entities.py +103 -0
  480. infinity_context_core/domain/entity_policies.py +119 -0
  481. infinity_context_core/domain/entity_types.py +150 -0
  482. infinity_context_core/domain/errors.py +53 -0
  483. infinity_context_core/domain/events.py +14 -0
  484. infinity_context_core/domain/extraction.py +529 -0
  485. infinity_context_core/domain/idempotency.py +12 -0
  486. infinity_context_core/domain/memory_item_entities.py +426 -0
  487. infinity_context_core/domain/source_refs.py +91 -0
  488. infinity_context_core/domain/suggestion_entities.py +236 -0
  489. infinity_context_core/domain/taxonomy.py +121 -0
  490. infinity_context_core/domain/usage.py +221 -0
  491. infinity_context_core/domain/workspace_entities.py +269 -0
  492. infinity_context_core/features/__init__.py +7 -0
  493. infinity_context_core/features/context_building/__init__.py +3 -0
  494. infinity_context_core/features/context_building/application/__init__.py +92 -0
  495. infinity_context_core/features/context_building/application/candidate_fusion.py +136 -0
  496. infinity_context_core/features/context_building/application/coverage_reservation_selector.py +212 -0
  497. infinity_context_core/features/context_building/application/handlers.py +155 -0
  498. infinity_context_core/features/context_building/application/inference_evidence_reservation.py +592 -0
  499. infinity_context_core/features/context_building/application/provider_pipeline.py +61 -0
  500. infinity_context_core/features/context_building/application/queries.py +107 -0
  501. infinity_context_core/features/context_building/application/use_cases.py +78 -0
  502. infinity_context_core/features/context_building/domain/__init__.py +95 -0
  503. infinity_context_core/features/context_building/domain/budget.py +97 -0
  504. infinity_context_core/features/context_building/domain/context.py +157 -0
  505. infinity_context_core/features/context_building/domain/evidence_obligations.py +60 -0
  506. infinity_context_core/features/context_building/domain/feature.py +15 -0
  507. infinity_context_core/features/context_building/domain/prompt_sections.py +150 -0
  508. infinity_context_core/features/context_building/domain/query_pipeline.py +276 -0
  509. infinity_context_core/features/context_building/domain/rendering.py +102 -0
  510. infinity_context_core/features/context_building/ports/__init__.py +8 -0
  511. infinity_context_core/features/context_building/ports/candidates.py +36 -0
  512. infinity_context_core/features/context_building/public.py +165 -0
  513. infinity_context_core/features/context_building/tests/test_application_ports_public_skeleton.py +575 -0
  514. infinity_context_core/features/context_building/tests/test_domain_context_building_skeleton.py +402 -0
  515. infinity_context_core/features/document_ingestion/__init__.py +3 -0
  516. infinity_context_core/features/document_ingestion/application/__init__.py +31 -0
  517. infinity_context_core/features/document_ingestion/application/commands.py +62 -0
  518. infinity_context_core/features/document_ingestion/application/use_cases.py +273 -0
  519. infinity_context_core/features/document_ingestion/domain/__init__.py +61 -0
  520. infinity_context_core/features/document_ingestion/domain/chunking.py +297 -0
  521. infinity_context_core/features/document_ingestion/domain/errors.py +22 -0
  522. infinity_context_core/features/document_ingestion/domain/feature.py +18 -0
  523. infinity_context_core/features/document_ingestion/domain/source_document.py +228 -0
  524. infinity_context_core/features/document_ingestion/ports/__init__.py +21 -0
  525. infinity_context_core/features/document_ingestion/ports/indexing.py +51 -0
  526. infinity_context_core/features/document_ingestion/ports/repositories.py +56 -0
  527. infinity_context_core/features/document_ingestion/public.py +95 -0
  528. infinity_context_core/features/document_ingestion/tests/test_application_handlers.py +226 -0
  529. infinity_context_core/features/document_ingestion/tests/test_application_ports_public_skeleton.py +334 -0
  530. infinity_context_core/features/document_ingestion/tests/test_domain_chunking_skeleton.py +79 -0
  531. infinity_context_core/features/document_ingestion/tests/test_domain_source_document_skeleton.py +101 -0
  532. infinity_context_core/features/memory_facts/__init__.py +3 -0
  533. infinity_context_core/features/memory_facts/application/__init__.py +37 -0
  534. infinity_context_core/features/memory_facts/application/commands.py +89 -0
  535. infinity_context_core/features/memory_facts/application/handlers.py +241 -0
  536. infinity_context_core/features/memory_facts/application/use_cases.py +47 -0
  537. infinity_context_core/features/memory_facts/domain/__init__.py +35 -0
  538. infinity_context_core/features/memory_facts/domain/fact.py +98 -0
  539. infinity_context_core/features/memory_facts/domain/feature.py +15 -0
  540. infinity_context_core/features/memory_facts/ports/__init__.py +31 -0
  541. infinity_context_core/features/memory_facts/ports/clock.py +14 -0
  542. infinity_context_core/features/memory_facts/ports/ids.py +19 -0
  543. infinity_context_core/features/memory_facts/ports/outbox.py +26 -0
  544. infinity_context_core/features/memory_facts/ports/repositories.py +30 -0
  545. infinity_context_core/features/memory_facts/ports/unit_of_work.py +43 -0
  546. infinity_context_core/features/memory_facts/public.py +79 -0
  547. infinity_context_core/features/memory_facts/tests/test_application_handlers.py +332 -0
  548. infinity_context_core/features/memory_facts/tests/test_application_ports_public_skeleton.py +318 -0
  549. infinity_context_core/features/memory_facts/tests/test_domain_fact_skeleton.py +222 -0
  550. infinity_context_core/features/memory_scopes/__init__.py +3 -0
  551. infinity_context_core/features/memory_scopes/application/__init__.py +55 -0
  552. infinity_context_core/features/memory_scopes/application/commands.py +102 -0
  553. infinity_context_core/features/memory_scopes/application/errors.py +27 -0
  554. infinity_context_core/features/memory_scopes/application/handlers.py +226 -0
  555. infinity_context_core/features/memory_scopes/application/use_cases.py +68 -0
  556. infinity_context_core/features/memory_scopes/domain/__init__.py +59 -0
  557. infinity_context_core/features/memory_scopes/domain/errors.py +22 -0
  558. infinity_context_core/features/memory_scopes/domain/feature.py +18 -0
  559. infinity_context_core/features/memory_scopes/domain/policies.py +197 -0
  560. infinity_context_core/features/memory_scopes/domain/scope.py +178 -0
  561. infinity_context_core/features/memory_scopes/ports/__init__.py +25 -0
  562. infinity_context_core/features/memory_scopes/ports/clock.py +14 -0
  563. infinity_context_core/features/memory_scopes/ports/ids.py +13 -0
  564. infinity_context_core/features/memory_scopes/ports/repositories.py +40 -0
  565. infinity_context_core/features/memory_scopes/ports/unit_of_work.py +39 -0
  566. infinity_context_core/features/memory_scopes/public.py +111 -0
  567. infinity_context_core/features/memory_scopes/tests/test_application_ports_public_skeleton.py +824 -0
  568. infinity_context_core/features/memory_scopes/tests/test_domain_scope_skeleton.py +428 -0
  569. infinity_context_core/memory_scope_snapshot_preview.py +863 -0
  570. infinity_context_core/memory_scope_snapshots.py +206 -0
  571. infinity_context_core/ports/__init__.py +164 -0
  572. infinity_context_core/ports/adapters.py +217 -0
  573. infinity_context_core/ports/assets.py +270 -0
  574. infinity_context_core/ports/auth.py +68 -0
  575. infinity_context_core/ports/auto_memory.py +71 -0
  576. infinity_context_core/ports/benchmark_runs.py +205 -0
  577. infinity_context_core/ports/capabilities.py +324 -0
  578. infinity_context_core/ports/captures.py +53 -0
  579. infinity_context_core/ports/clock.py +11 -0
  580. infinity_context_core/ports/derived_projection_policy.py +72 -0
  581. infinity_context_core/ports/extraction.py +201 -0
  582. infinity_context_core/ports/graph_evidence.py +170 -0
  583. infinity_context_core/ports/ids.py +11 -0
  584. infinity_context_core/ports/projection_fence.py +27 -0
  585. infinity_context_core/ports/repositories.py +563 -0
  586. infinity_context_core/ports/transcription.py +63 -0
  587. infinity_context_core/ports/unit_of_work.py +77 -0
  588. infinity_context_core/ports/usage.py +36 -0
  589. infinity_context_core/ports/vector_projection_evidence.py +230 -0
  590. infinity_context_core/ports/vision.py +36 -0
  591. infinity_context_core/processes/__init__.py +3 -0
  592. infinity_context_core/processes/document_memory_pipeline/__init__.py +3 -0
  593. infinity_context_core/processes/document_memory_pipeline/contracts.py +44 -0
  594. infinity_context_core/processes/document_memory_pipeline/process.py +72 -0
  595. infinity_context_core/processes/document_memory_pipeline/public.py +21 -0
  596. infinity_context_core/reporting.py +105 -0
  597. infinity_context_mcp/__init__.py +5 -0
  598. infinity_context_mcp/__main__.py +6 -0
  599. infinity_context_mcp/adapters/__init__.py +1 -0
  600. infinity_context_mcp/adapters/http_gateway.py +940 -0
  601. infinity_context_mcp/agent_behavior_bench.py +1731 -0
  602. infinity_context_mcp/agent_behavior_bench_metrics.py +419 -0
  603. infinity_context_mcp/agent_behavior_bench_redaction.py +149 -0
  604. infinity_context_mcp/agent_behavior_bench_types.py +127 -0
  605. infinity_context_mcp/agent_behavior_scenarios.py +35 -0
  606. infinity_context_mcp/agent_behavior_scenarios_core.py +484 -0
  607. infinity_context_mcp/agent_behavior_scenarios_realistic.py +473 -0
  608. infinity_context_mcp/agent_behavior_scenarios_transcript.py +196 -0
  609. infinity_context_mcp/agent_behavior_transcripts.py +231 -0
  610. infinity_context_mcp/agent_behavior_types.py +66 -0
  611. infinity_context_mcp/agent_behavior_utils.py +29 -0
  612. infinity_context_mcp/application/__init__.py +1 -0
  613. infinity_context_mcp/application/context_link_review.py +109 -0
  614. infinity_context_mcp/application/local_runtime.py +473 -0
  615. infinity_context_mcp/application/normalization.py +36 -0
  616. infinity_context_mcp/application/obsidian.py +568 -0
  617. infinity_context_mcp/application/policy.py +160 -0
  618. infinity_context_mcp/application/ports.py +324 -0
  619. infinity_context_mcp/application/prepare.py +254 -0
  620. infinity_context_mcp/application/readiness.py +71 -0
  621. infinity_context_mcp/application/review_batch.py +62 -0
  622. infinity_context_mcp/application/service.py +701 -0
  623. infinity_context_mcp/application/service_base.py +29 -0
  624. infinity_context_mcp/application/service_constants.py +89 -0
  625. infinity_context_mcp/application/service_context_links.py +224 -0
  626. infinity_context_mcp/application/service_duplicates.py +62 -0
  627. infinity_context_mcp/application/service_facts.py +461 -0
  628. infinity_context_mcp/application/service_helpers.py +179 -0
  629. infinity_context_mcp/application/service_lifecycle.py +348 -0
  630. infinity_context_mcp/application/service_policy.py +74 -0
  631. infinity_context_mcp/application/service_query.py +384 -0
  632. infinity_context_mcp/application/service_resources.py +192 -0
  633. infinity_context_mcp/application/service_response.py +95 -0
  634. infinity_context_mcp/application/service_scope.py +132 -0
  635. infinity_context_mcp/application/service_suggestions.py +820 -0
  636. infinity_context_mcp/application/suggest_batch.py +71 -0
  637. infinity_context_mcp/application/usage_guide.py +74 -0
  638. infinity_context_mcp/bench.py +279 -0
  639. infinity_context_mcp/config.py +213 -0
  640. infinity_context_mcp/domain/__init__.py +1 -0
  641. infinity_context_mcp/domain/context_links.py +117 -0
  642. infinity_context_mcp/domain/local_runtime_models.py +56 -0
  643. infinity_context_mcp/domain/memory_browser.py +35 -0
  644. infinity_context_mcp/domain/models.py +1036 -0
  645. infinity_context_mcp/domain/obsidian_models.py +67 -0
  646. infinity_context_mcp/domain/policy.py +112 -0
  647. infinity_context_mcp/domain/prepare_models.py +24 -0
  648. infinity_context_mcp/plugin_hook.py +873 -0
  649. infinity_context_mcp/plugin_hook_settings.py +253 -0
  650. infinity_context_mcp/server.py +133 -0
  651. infinity_context_mcp/server_context_tools.py +404 -0
  652. infinity_context_mcp/server_fact_tools.py +928 -0
  653. infinity_context_mcp/server_hardening.py +54 -0
  654. infinity_context_mcp/server_local_runtime_tools.py +164 -0
  655. infinity_context_mcp/server_obsidian_tools.py +380 -0
  656. infinity_context_mcp/server_query_tools.py +626 -0
  657. infinity_context_mcp/server_request_mapping.py +73 -0
  658. infinity_context_mcp/server_resources.py +187 -0
  659. infinity_context_mcp/server_response.py +26 -0
  660. infinity_context_obsidian/__init__.py +5 -0
  661. infinity_context_obsidian/cli.py +594 -0
  662. infinity_context_obsidian/conflicts.py +110 -0
  663. infinity_context_obsidian/doctor.py +319 -0
  664. infinity_context_obsidian/domain.py +92 -0
  665. infinity_context_obsidian/frontmatter.py +68 -0
  666. infinity_context_obsidian/gateway.py +66 -0
  667. infinity_context_obsidian/layout.py +179 -0
  668. infinity_context_obsidian/note_format.py +159 -0
  669. infinity_context_obsidian/plugin_bundle/__init__.py +1 -0
  670. infinity_context_obsidian/plugin_bundle/main.js +5 -0
  671. infinity_context_obsidian/plugin_bundle/manifest.json +10 -0
  672. infinity_context_obsidian/plugin_bundle/styles.css +123 -0
  673. infinity_context_obsidian/plugin_install.py +139 -0
  674. infinity_context_obsidian/ports.py +72 -0
  675. infinity_context_obsidian/setup.py +202 -0
  676. infinity_context_obsidian/state.py +85 -0
  677. infinity_context_obsidian/sync.py +741 -0
  678. infinity_context_obsidian/vault.py +61 -0
  679. infinity_context_sdk/__init__.py +1049 -0
  680. infinity_context_sdk/_payloads.py +401 -0
  681. infinity_context_sdk/_redaction.py +31 -0
  682. infinity_context_sdk/anchors.py +249 -0
  683. infinity_context_sdk/assets.py +554 -0
  684. infinity_context_sdk/capabilities.py +199 -0
  685. infinity_context_sdk/context.py +540 -0
  686. infinity_context_sdk/context_bundle_diagnostics.py +905 -0
  687. infinity_context_sdk/context_payload_utils.py +219 -0
  688. infinity_context_sdk/export.py +123 -0
  689. infinity_context_sdk/scopes.py +74 -0
  690. infinity_context_sdk/thread_memory.py +55 -0
  691. infinity_context_server/__init__.py +5 -0
  692. infinity_context_server/__main__.py +19 -0
  693. infinity_context_server/admin.py +832 -0
  694. infinity_context_server/admin_invariants.py +388 -0
  695. infinity_context_server/admin_outbox.py +130 -0
  696. infinity_context_server/admin_projection_repair.py +374 -0
  697. infinity_context_server/api/__init__.py +1 -0
  698. infinity_context_server/api/auth.py +432 -0
  699. infinity_context_server/api/dependencies.py +9 -0
  700. infinity_context_server/api/errors.py +94 -0
  701. infinity_context_server/api/legacy_client.py +356 -0
  702. infinity_context_server/api/policy.py +35 -0
  703. infinity_context_server/api/public_payload.py +27 -0
  704. infinity_context_server/api/v1/__init__.py +54 -0
  705. infinity_context_server/api/v1/anchors.py +469 -0
  706. infinity_context_server/api/v1/assets.py +409 -0
  707. infinity_context_server/api/v1/capabilities.py +144 -0
  708. infinity_context_server/api/v1/captures.py +329 -0
  709. infinity_context_server/api/v1/context.py +362 -0
  710. infinity_context_server/api/v1/context_links.py +611 -0
  711. infinity_context_server/api/v1/diagnostics.py +333 -0
  712. infinity_context_server/api/v1/digest.py +107 -0
  713. infinity_context_server/api/v1/documents.py +164 -0
  714. infinity_context_server/api/v1/episodes.py +146 -0
  715. infinity_context_server/api/v1/export.py +184 -0
  716. infinity_context_server/api/v1/facts.py +265 -0
  717. infinity_context_server/api/v1/health.py +24 -0
  718. infinity_context_server/api/v1/insights.py +100 -0
  719. infinity_context_server/api/v1/internal_memory_comparison_runs.py +406 -0
  720. infinity_context_server/api/v1/memory_browser.py +183 -0
  721. infinity_context_server/api/v1/operations.py +81 -0
  722. infinity_context_server/api/v1/scope_resolution.py +389 -0
  723. infinity_context_server/api/v1/source_refs.py +111 -0
  724. infinity_context_server/api/v1/spaces_memory_scopes.py +141 -0
  725. infinity_context_server/api/v1/suggestions.py +237 -0
  726. infinity_context_server/api/v1/thread_memory.py +74 -0
  727. infinity_context_server/api/v1/usage.py +75 -0
  728. infinity_context_server/api/v1/users.py +164 -0
  729. infinity_context_server/auth_scope.py +492 -0
  730. infinity_context_server/auth_tokens.py +283 -0
  731. infinity_context_server/backpressure.py +47 -0
  732. infinity_context_server/benchmark_projection_absence.py +155 -0
  733. infinity_context_server/benchmark_projection_delete_outbox.py +162 -0
  734. infinity_context_server/composition.py +897 -0
  735. infinity_context_server/config.py +223 -0
  736. infinity_context_server/db.py +39 -0
  737. infinity_context_server/deployment_readiness.py +205 -0
  738. infinity_context_server/derived_identity_evidence.py +987 -0
  739. infinity_context_server/derived_identity_target.py +39 -0
  740. infinity_context_server/derived_projection_policy.py +85 -0
  741. infinity_context_server/diagnostics.py +536 -0
  742. infinity_context_server/doctor.py +71 -0
  743. infinity_context_server/eval.py +3066 -0
  744. infinity_context_server/eval_auto_memory.py +1735 -0
  745. infinity_context_server/eval_auto_memory_metrics.py +224 -0
  746. infinity_context_server/eval_auto_memory_types.py +75 -0
  747. infinity_context_server/eval_case_catalog.py +1590 -0
  748. infinity_context_server/eval_case_runner.py +1100 -0
  749. infinity_context_server/eval_common.py +336 -0
  750. infinity_context_server/eval_constants.py +310 -0
  751. infinity_context_server/eval_fixtures.py +150 -0
  752. infinity_context_server/eval_graph.py +92 -0
  753. infinity_context_server/eval_hybrid.py +99 -0
  754. infinity_context_server/eval_multimodal_offline.py +697 -0
  755. infinity_context_server/eval_prompt_contract.py +402 -0
  756. infinity_context_server/eval_scorecard.py +2627 -0
  757. infinity_context_server/eval_semantic_linking.py +1526 -0
  758. infinity_context_server/eval_semantic_retrieval_api_canary.py +405 -0
  759. infinity_context_server/eval_semantic_retrieval_canary.py +359 -0
  760. infinity_context_server/eval_types.py +88 -0
  761. infinity_context_server/evidence_bundle.py +523 -0
  762. infinity_context_server/extraction_capabilities.py +1234 -0
  763. infinity_context_server/features/__init__.py +3 -0
  764. infinity_context_server/features/context_building/__init__.py +5 -0
  765. infinity_context_server/features/context_building/benchmark_budget.py +29 -0
  766. infinity_context_server/features/context_building/composition.py +49 -0
  767. infinity_context_server/features/context_building/context_requests.py +151 -0
  768. infinity_context_server/features/context_building/contracts.py +96 -0
  769. infinity_context_server/features/context_building/digest_api_responses.py +225 -0
  770. infinity_context_server/features/context_building/digest_requests.py +65 -0
  771. infinity_context_server/features/context_building/insights_api_responses.py +132 -0
  772. infinity_context_server/features/context_building/legacy_api_responses.py +875 -0
  773. infinity_context_server/features/context_building/mappers.py +205 -0
  774. infinity_context_server/features/context_building/public.py +66 -0
  775. infinity_context_server/features/context_building/routes.py +44 -0
  776. infinity_context_server/features/document_ingestion/__init__.py +5 -0
  777. infinity_context_server/features/document_ingestion/asset_requests.py +45 -0
  778. infinity_context_server/features/document_ingestion/asset_responses.py +365 -0
  779. infinity_context_server/features/document_ingestion/composition.py +49 -0
  780. infinity_context_server/features/document_ingestion/contracts.py +104 -0
  781. infinity_context_server/features/document_ingestion/mappers.py +386 -0
  782. infinity_context_server/features/document_ingestion/public.py +58 -0
  783. infinity_context_server/features/document_ingestion/routes.py +44 -0
  784. infinity_context_server/features/memory_facts/__init__.py +7 -0
  785. infinity_context_server/features/memory_facts/compatibility.py +204 -0
  786. infinity_context_server/features/memory_facts/composition.py +72 -0
  787. infinity_context_server/features/memory_facts/contracts.py +187 -0
  788. infinity_context_server/features/memory_facts/mappers.py +620 -0
  789. infinity_context_server/features/memory_facts/public.py +168 -0
  790. infinity_context_server/features/memory_facts/routes.py +118 -0
  791. infinity_context_server/features/memory_facts/suggestion_requests.py +283 -0
  792. infinity_context_server/features/memory_facts/suggestion_responses.py +382 -0
  793. infinity_context_server/features/memory_scopes/__init__.py +5 -0
  794. infinity_context_server/features/memory_scopes/compatibility.py +231 -0
  795. infinity_context_server/features/memory_scopes/composition.py +49 -0
  796. infinity_context_server/features/memory_scopes/contracts.py +103 -0
  797. infinity_context_server/features/memory_scopes/graph_export_responses.py +93 -0
  798. infinity_context_server/features/memory_scopes/mappers.py +290 -0
  799. infinity_context_server/features/memory_scopes/public.py +136 -0
  800. infinity_context_server/features/memory_scopes/routes.py +185 -0
  801. infinity_context_server/features/memory_scopes/snapshot_compatibility.py +146 -0
  802. infinity_context_server/features/memory_scopes/thread_memory_requests.py +127 -0
  803. infinity_context_server/llm_provider_readiness.py +267 -0
  804. infinity_context_server/longmemeval_session_identity.py +167 -0
  805. infinity_context_server/main.py +68 -0
  806. infinity_context_server/mem0_benchmark_auth_challenge.py +160 -0
  807. infinity_context_server/memora_comparison.py +658 -0
  808. infinity_context_server/memory_comparison_answer_context.py +2442 -0
  809. infinity_context_server/memory_comparison_answer_context_backfill.py +1027 -0
  810. infinity_context_server/memory_comparison_answer_context_diagnostics.py +694 -0
  811. infinity_context_server/memory_comparison_answer_context_inspection.py +103 -0
  812. infinity_context_server/memory_comparison_answer_context_retrieval_slice.py +175 -0
  813. infinity_context_server/memory_comparison_answer_context_risks.py +223 -0
  814. infinity_context_server/memory_comparison_backend_target.py +41 -0
  815. infinity_context_server/memory_comparison_benchmark.py +651 -0
  816. infinity_context_server/memory_comparison_benchmark_compact.py +387 -0
  817. infinity_context_server/memory_comparison_benchmark_compact_fast_gate.py +387 -0
  818. infinity_context_server/memory_comparison_benchmark_compact_samples.py +931 -0
  819. infinity_context_server/memory_comparison_benchmark_config.py +105 -0
  820. infinity_context_server/memory_comparison_benchmark_control.py +215 -0
  821. infinity_context_server/memory_comparison_benchmark_execution.py +238 -0
  822. infinity_context_server/memory_comparison_benchmark_identity.py +65 -0
  823. infinity_context_server/memory_comparison_benchmark_metrics.py +686 -0
  824. infinity_context_server/memory_comparison_benchmark_replay.py +400 -0
  825. infinity_context_server/memory_comparison_benchmark_shared.py +284 -0
  826. infinity_context_server/memory_comparison_bounded_provider.py +295 -0
  827. infinity_context_server/memory_comparison_bundle_partial_support.py +107 -0
  828. infinity_context_server/memory_comparison_bundle_planner.py +3521 -0
  829. infinity_context_server/memory_comparison_bundle_source_windows.py +247 -0
  830. infinity_context_server/memory_comparison_candidate_features.py +2014 -0
  831. infinity_context_server/memory_comparison_candidate_fusion.py +893 -0
  832. infinity_context_server/memory_comparison_candidate_risks.py +124 -0
  833. infinity_context_server/memory_comparison_canonical_source_hash.py +109 -0
  834. infinity_context_server/memory_comparison_case_identity.py +100 -0
  835. infinity_context_server/memory_comparison_case_loader.py +123 -0
  836. infinity_context_server/memory_comparison_chat_completions.py +402 -0
  837. infinity_context_server/memory_comparison_clean_state.py +607 -0
  838. infinity_context_server/memory_comparison_clean_state_http.py +120 -0
  839. infinity_context_server/memory_comparison_clean_state_runner.py +103 -0
  840. infinity_context_server/memory_comparison_cli.py +295 -0
  841. infinity_context_server/memory_comparison_codex_canary.py +315 -0
  842. infinity_context_server/memory_comparison_codex_llm.py +345 -0
  843. infinity_context_server/memory_comparison_compact_gap_report.py +289 -0
  844. infinity_context_server/memory_comparison_comparative_preference.py +71 -0
  845. infinity_context_server/memory_comparison_conversation_ingestion.py +198 -0
  846. infinity_context_server/memory_comparison_evidence.py +877 -0
  847. infinity_context_server/memory_comparison_evidence_commitment.py +20 -0
  848. infinity_context_server/memory_comparison_failure_diagnostics.py +809 -0
  849. infinity_context_server/memory_comparison_full_canonical_evidence.py +746 -0
  850. infinity_context_server/memory_comparison_full_canonical_source_evidence_trust.py +450 -0
  851. infinity_context_server/memory_comparison_full_delete_evidence.py +670 -0
  852. infinity_context_server/memory_comparison_full_delete_evidence_trust.py +441 -0
  853. infinity_context_server/memory_comparison_full_delete_evidence_witnesses.py +308 -0
  854. infinity_context_server/memory_comparison_full_execution_validation.py +697 -0
  855. infinity_context_server/memory_comparison_full_execution_validation_slots.py +655 -0
  856. infinity_context_server/memory_comparison_full_methodology.py +556 -0
  857. infinity_context_server/memory_comparison_full_policy_component_validation.py +899 -0
  858. infinity_context_server/memory_comparison_full_profiles.py +398 -0
  859. infinity_context_server/memory_comparison_full_run_component_sets.py +304 -0
  860. infinity_context_server/memory_comparison_full_run_components.py +787 -0
  861. infinity_context_server/memory_comparison_full_run_evidence.py +759 -0
  862. infinity_context_server/memory_comparison_full_scope.py +244 -0
  863. infinity_context_server/memory_comparison_full_source_evidence.py +866 -0
  864. infinity_context_server/memory_comparison_full_verdict.py +252 -0
  865. infinity_context_server/memory_comparison_gold_blind.py +654 -0
  866. infinity_context_server/memory_comparison_gold_blind_answer_contract.py +492 -0
  867. infinity_context_server/memory_comparison_gold_blind_contract.py +760 -0
  868. infinity_context_server/memory_comparison_gold_blind_dispatch_models.py +34 -0
  869. infinity_context_server/memory_comparison_gold_blind_execution_validation.py +170 -0
  870. infinity_context_server/memory_comparison_gold_blind_judge_binding.py +167 -0
  871. infinity_context_server/memory_comparison_gold_blind_judge_capability.py +387 -0
  872. infinity_context_server/memory_comparison_gold_blind_retrieval_contract.py +413 -0
  873. infinity_context_server/memory_comparison_gold_blind_run_proof.py +880 -0
  874. infinity_context_server/memory_comparison_gold_blind_run_validation.py +109 -0
  875. infinity_context_server/memory_comparison_gold_blind_validation.py +319 -0
  876. infinity_context_server/memory_comparison_http.py +921 -0
  877. infinity_context_server/memory_comparison_http_ingest_observation.py +710 -0
  878. infinity_context_server/memory_comparison_http_ingest_request.py +204 -0
  879. infinity_context_server/memory_comparison_intent.py +2925 -0
  880. infinity_context_server/memory_comparison_latency.py +455 -0
  881. infinity_context_server/memory_comparison_llm.py +473 -0
  882. infinity_context_server/memory_comparison_location_roles.py +169 -0
  883. infinity_context_server/memory_comparison_locomo_cases.py +328 -0
  884. infinity_context_server/memory_comparison_locomo_expected_turn.py +297 -0
  885. infinity_context_server/memory_comparison_locomo_transport.py +873 -0
  886. infinity_context_server/memory_comparison_longmemeval_cases.py +339 -0
  887. infinity_context_server/memory_comparison_managed_attestation.py +771 -0
  888. infinity_context_server/memory_comparison_managed_benchmark_registry_abort_http.py +120 -0
  889. infinity_context_server/memory_comparison_managed_benchmark_registry_contracts.py +715 -0
  890. infinity_context_server/memory_comparison_managed_benchmark_registry_http.py +855 -0
  891. infinity_context_server/memory_comparison_managed_benchmark_registry_recovery_http.py +164 -0
  892. infinity_context_server/memory_comparison_managed_benchmark_registry_wire.py +890 -0
  893. infinity_context_server/memory_comparison_managed_composite_assembler.py +524 -0
  894. infinity_context_server/memory_comparison_managed_corpus_projection.py +653 -0
  895. infinity_context_server/memory_comparison_managed_execution_receipts.py +991 -0
  896. infinity_context_server/memory_comparison_managed_http_derived_evidence.py +724 -0
  897. infinity_context_server/memory_comparison_managed_http_exact_cleanup.py +476 -0
  898. infinity_context_server/memory_comparison_managed_http_execution.py +673 -0
  899. infinity_context_server/memory_comparison_managed_http_lifecycle.py +905 -0
  900. infinity_context_server/memory_comparison_managed_http_lifecycle_credentials.py +83 -0
  901. infinity_context_server/memory_comparison_managed_http_lifecycle_evidence.py +366 -0
  902. infinity_context_server/memory_comparison_managed_http_lifecycle_registration.py +106 -0
  903. infinity_context_server/memory_comparison_managed_http_policy_lifecycle.py +894 -0
  904. infinity_context_server/memory_comparison_managed_http_policy_material_projection.py +315 -0
  905. infinity_context_server/memory_comparison_managed_http_policy_receipts.py +219 -0
  906. infinity_context_server/memory_comparison_managed_http_policy_registry_evidence.py +177 -0
  907. infinity_context_server/memory_comparison_managed_http_policy_requirements.py +824 -0
  908. infinity_context_server/memory_comparison_managed_http_policy_support.py +108 -0
  909. infinity_context_server/memory_comparison_managed_http_policy_validation.py +762 -0
  910. infinity_context_server/memory_comparison_managed_ingest_manifest.py +449 -0
  911. infinity_context_server/memory_comparison_managed_live_admission.py +886 -0
  912. infinity_context_server/memory_comparison_managed_live_cli.py +536 -0
  913. infinity_context_server/memory_comparison_managed_live_composition.py +636 -0
  914. infinity_context_server/memory_comparison_managed_llm_execution.py +850 -0
  915. infinity_context_server/memory_comparison_managed_llm_execution_dispatch.py +269 -0
  916. infinity_context_server/memory_comparison_managed_llm_execution_ports.py +225 -0
  917. infinity_context_server/memory_comparison_managed_mem0_auth.py +65 -0
  918. infinity_context_server/memory_comparison_managed_mem0_runtime_authority.py +241 -0
  919. infinity_context_server/memory_comparison_managed_mem0_runtime_http.py +541 -0
  920. infinity_context_server/memory_comparison_managed_plan_builder.py +685 -0
  921. infinity_context_server/memory_comparison_managed_preflight.py +907 -0
  922. infinity_context_server/memory_comparison_managed_preflight_cli.py +431 -0
  923. infinity_context_server/memory_comparison_managed_production_cli.py +325 -0
  924. infinity_context_server/memory_comparison_managed_production_composition.py +140 -0
  925. infinity_context_server/memory_comparison_managed_production_ports.py +194 -0
  926. infinity_context_server/memory_comparison_managed_production_runner.py +450 -0
  927. infinity_context_server/memory_comparison_managed_projection_manifest.py +438 -0
  928. infinity_context_server/memory_comparison_managed_provider_calls.py +450 -0
  929. infinity_context_server/memory_comparison_managed_quality_projection.py +347 -0
  930. infinity_context_server/memory_comparison_managed_registry_policy_lifecycle.py +677 -0
  931. infinity_context_server/memory_comparison_managed_run.py +892 -0
  932. infinity_context_server/memory_comparison_managed_run_contract.py +454 -0
  933. infinity_context_server/memory_comparison_managed_run_ports.py +79 -0
  934. infinity_context_server/memory_comparison_managed_runtime_credentials.py +916 -0
  935. infinity_context_server/memory_comparison_managed_runtime_credentials_capability.py +361 -0
  936. infinity_context_server/memory_comparison_managed_runtime_credentials_context.py +132 -0
  937. infinity_context_server/memory_comparison_managed_runtime_credentials_integrity.py +213 -0
  938. infinity_context_server/memory_comparison_managed_runtime_credentials_models.py +81 -0
  939. infinity_context_server/memory_comparison_managed_runtime_credentials_targets.py +28 -0
  940. infinity_context_server/memory_comparison_mem0_contract.py +491 -0
  941. infinity_context_server/memory_comparison_mem0_http_observation.py +296 -0
  942. infinity_context_server/memory_comparison_mem0_locomo_prompts.py +328 -0
  943. infinity_context_server/memory_comparison_mem0_longmemeval_prompts.py +365 -0
  944. infinity_context_server/memory_comparison_mem0_official_chat.py +504 -0
  945. infinity_context_server/memory_comparison_mem0_official_prompt_renderer.py +307 -0
  946. infinity_context_server/memory_comparison_mem0_oss_contract.py +456 -0
  947. infinity_context_server/memory_comparison_mem0_oss_manifest.py +563 -0
  948. infinity_context_server/memory_comparison_mem0_platform_contract.py +467 -0
  949. infinity_context_server/memory_comparison_mem0_runtime_attestation.py +861 -0
  950. infinity_context_server/memory_comparison_models.py +398 -0
  951. infinity_context_server/memory_comparison_openai_official_transport.py +525 -0
  952. infinity_context_server/memory_comparison_preflight.py +1147 -0
  953. infinity_context_server/memory_comparison_probe_transport.py +154 -0
  954. infinity_context_server/memory_comparison_prompt_policy.py +603 -0
  955. infinity_context_server/memory_comparison_provider_provenance.py +392 -0
  956. infinity_context_server/memory_comparison_quality_accessors.py +339 -0
  957. infinity_context_server/memory_comparison_quality_actionable_gaps.py +2038 -0
  958. infinity_context_server/memory_comparison_quality_answer_context_gaps.py +450 -0
  959. infinity_context_server/memory_comparison_quality_answer_context_risks.py +75 -0
  960. infinity_context_server/memory_comparison_quality_bundle_gaps.py +860 -0
  961. infinity_context_server/memory_comparison_quality_bundle_table.py +805 -0
  962. infinity_context_server/memory_comparison_quality_diagnostics.py +2798 -0
  963. infinity_context_server/memory_comparison_quality_fusion.py +241 -0
  964. infinity_context_server/memory_comparison_quality_query_plan_gaps.py +533 -0
  965. infinity_context_server/memory_comparison_quality_query_role_gaps.py +845 -0
  966. infinity_context_server/memory_comparison_quality_query_roles.py +815 -0
  967. infinity_context_server/memory_comparison_quality_rerank_gaps.py +492 -0
  968. infinity_context_server/memory_comparison_quality_selected_weakness.py +544 -0
  969. infinity_context_server/memory_comparison_quality_support.py +921 -0
  970. infinity_context_server/memory_comparison_query_integrity.py +301 -0
  971. infinity_context_server/memory_comparison_query_plan.py +524 -0
  972. infinity_context_server/memory_comparison_query_terms.py +2055 -0
  973. infinity_context_server/memory_comparison_relation_support.py +2100 -0
  974. infinity_context_server/memory_comparison_rerank.py +3209 -0
  975. infinity_context_server/memory_comparison_rerank_intents.py +405 -0
  976. infinity_context_server/memory_comparison_rerank_policies.py +1726 -0
  977. infinity_context_server/memory_comparison_rerank_policy.py +675 -0
  978. infinity_context_server/memory_comparison_rerank_shapes.py +152 -0
  979. infinity_context_server/memory_comparison_rerank_terms.py +973 -0
  980. infinity_context_server/memory_comparison_rerank_text.py +722 -0
  981. infinity_context_server/memory_comparison_retrieval_policy.py +108 -0
  982. infinity_context_server/memory_comparison_retrieval_width.py +383 -0
  983. infinity_context_server/memory_comparison_service_probe.py +278 -0
  984. infinity_context_server/memory_comparison_service_probes.py +547 -0
  985. infinity_context_server/memory_comparison_session_identity_contract.py +328 -0
  986. infinity_context_server/memory_comparison_source_identity.py +1655 -0
  987. infinity_context_server/memory_comparison_subscription_chat.py +717 -0
  988. infinity_context_server/memory_comparison_subscription_live_probe.py +323 -0
  989. infinity_context_server/memory_comparison_subscription_probe.py +368 -0
  990. infinity_context_server/memory_comparison_target_identity.py +42 -0
  991. infinity_context_server/memory_comparison_temporal_grounding.py +771 -0
  992. infinity_context_server/memory_comparison_token_budget_contract.py +665 -0
  993. infinity_context_server/memory_comparison_token_latency.py +136 -0
  994. infinity_context_server/memory_scope_transfer.py +933 -0
  995. infinity_context_server/memory_scope_transfer_assets.py +181 -0
  996. infinity_context_server/memory_scope_transfer_conflicts.py +565 -0
  997. infinity_context_server/memory_scope_transfer_context.py +295 -0
  998. infinity_context_server/memory_scope_transfer_extractions.py +390 -0
  999. infinity_context_server/memory_scope_transfer_facts.py +65 -0
  1000. infinity_context_server/memory_scope_transfer_records.py +630 -0
  1001. infinity_context_server/memory_scope_transfer_relations.py +84 -0
  1002. infinity_context_server/memory_scope_transfer_remap.py +710 -0
  1003. infinity_context_server/memory_scope_transfer_scope.py +267 -0
  1004. infinity_context_server/memory_scope_transfer_support.py +108 -0
  1005. infinity_context_server/memory_scope_transfer_temporal.py +24 -0
  1006. infinity_context_server/metrics.py +264 -0
  1007. infinity_context_server/official_public_benchmark.py +979 -0
  1008. infinity_context_server/pagination.py +60 -0
  1009. infinity_context_server/processes/__init__.py +38 -0
  1010. infinity_context_server/processes/extractions.py +40 -0
  1011. infinity_context_server/processes/outbox.py +47 -0
  1012. infinity_context_server/processes/projections.py +372 -0
  1013. infinity_context_server/provider_budget.py +59 -0
  1014. infinity_context_server/provider_circuit.py +333 -0
  1015. infinity_context_server/public_benchmark.py +2532 -0
  1016. infinity_context_server/public_benchmark_artifacts.py +91 -0
  1017. infinity_context_server/public_benchmark_case_diagnostics.py +231 -0
  1018. infinity_context_server/public_benchmark_checkpoint.py +751 -0
  1019. infinity_context_server/public_benchmark_environment.py +102 -0
  1020. infinity_context_server/public_benchmark_execution.py +445 -0
  1021. infinity_context_server/public_benchmark_http.py +39 -0
  1022. infinity_context_server/public_benchmark_manifest.py +129 -0
  1023. infinity_context_server/public_benchmark_metrics.py +645 -0
  1024. infinity_context_server/public_benchmark_metrics_policy.py +117 -0
  1025. infinity_context_server/public_benchmark_models.py +112 -0
  1026. infinity_context_server/public_benchmark_progress.py +224 -0
  1027. infinity_context_server/public_benchmark_results.py +76 -0
  1028. infinity_context_server/public_benchmark_run_diagnostics.py +163 -0
  1029. infinity_context_server/public_benchmark_selection.py +379 -0
  1030. infinity_context_server/public_benchmark_transport.py +62 -0
  1031. infinity_context_server/public_benchmark_unsupported.py +228 -0
  1032. infinity_context_server/ranked_evidence_answer_support.py +665 -0
  1033. infinity_context_server/ranked_evidence_document_seed_contract.py +30 -0
  1034. infinity_context_server/ranked_evidence_evaluator_helpers.py +135 -0
  1035. infinity_context_server/ranked_evidence_retrieval_request.py +36 -0
  1036. infinity_context_server/ranked_evidence_seed_case.py +109 -0
  1037. infinity_context_server/ranked_evidence_semantic_gate.py +980 -0
  1038. infinity_context_server/ranked_evidence_semantic_gate_cli.py +137 -0
  1039. infinity_context_server/ranked_evidence_semantic_gate_decision.py +163 -0
  1040. infinity_context_server/ranked_evidence_semantic_metrics.py +375 -0
  1041. infinity_context_server/ranked_evidence_session_identity.py +100 -0
  1042. infinity_context_server/storage_maintenance.py +187 -0
  1043. infinity_context_server/top_evidence_dataset_profile.py +442 -0
  1044. infinity_context_server/top_evidence_policy.py +274 -0
  1045. infinity_context_server/top_evidence_preflight.py +693 -0
  1046. infinity_context_server/web/assets/memory-browser-capture.js +252 -0
  1047. infinity_context_server/web/assets/memory-browser-operations.js +143 -0
  1048. infinity_context_server/web/assets/memory-browser-review.js +687 -0
  1049. infinity_context_server/web/assets/memory-browser.css +854 -0
  1050. infinity_context_server/web/assets/memory-browser.js +3299 -0
  1051. infinity_context_server/web/index.html +261 -0
  1052. infinity_context_server/web_ui.py +29 -0
  1053. infinity_context_server/worker.py +380 -0
@@ -0,0 +1,2038 @@
1
+ """Actionable gap ranking for memory-comparison fast-gate diagnostics."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping, Sequence
6
+
7
+ from infinity_context_server.memory_comparison_quality_accessors import (
8
+ count_mapping as _count_mapping,
9
+ )
10
+ from infinity_context_server.memory_comparison_quality_accessors import (
11
+ mapping as _mapping,
12
+ )
13
+ from infinity_context_server.memory_comparison_quality_accessors import (
14
+ positive_int as _positive_int,
15
+ )
16
+ from infinity_context_server.memory_comparison_quality_accessors import ratio as _ratio
17
+ from infinity_context_server.memory_comparison_quality_accessors import (
18
+ sequence as _sequence,
19
+ )
20
+ from infinity_context_server.memory_comparison_quality_accessors import (
21
+ str_tuple as _str_tuple,
22
+ )
23
+ from infinity_context_server.memory_comparison_source_identity import (
24
+ safe_item_id_for_output as _safe_item_id_for_output,
25
+ )
26
+ from infinity_context_server.memory_comparison_source_identity import (
27
+ safe_source_identity_ref as _safe_source_identity_ref,
28
+ )
29
+ from infinity_context_server.memory_comparison_source_identity import (
30
+ safe_turn_ref as _safe_exact_turn_ref,
31
+ )
32
+ from infinity_context_server.memory_comparison_source_identity import (
33
+ source_identity_refs_from_source_refs as _source_identity_refs_from_source_refs,
34
+ )
35
+
36
+ _MAX_RANKED_GAPS = 10
37
+ _MAX_SAMPLE_CASE_IDS = 5
38
+ _MAX_QUERY_PLAN_ACTIONABLE_SAMPLES = 3
39
+ _MAX_QUERY_PLAN_ACTIONABLE_SAMPLE_VALUES = 5
40
+ _MAX_QUERY_PLAN_ACTIONABLE_SAMPLE_TEXT = 128
41
+ _MAX_SELECTED_EVIDENCE_ACTIONABLE_SAMPLES = 3
42
+ _MAX_SELECTED_EVIDENCE_ACTIONABLE_SAMPLE_VALUES = 5
43
+ _MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLES = 3
44
+ _MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLE_VALUES = 5
45
+ _MAX_RERANK_SIGNAL_ACTIONABLE_SAMPLES = 3
46
+ _MAX_RERANK_SIGNAL_ACTIONABLE_SAMPLE_VALUES = 5
47
+ _MAX_EVIDENCE_RECALL_ACTIONABLE_SAMPLES = 3
48
+ _MAX_EVIDENCE_RECALL_ACTIONABLE_SAMPLE_VALUES = 5
49
+ _MAX_QUERY_ROLE_ACTIONABLE_SAMPLES = 3
50
+ _MAX_QUERY_ROLE_ACTIONABLE_SAMPLE_VALUES = 5
51
+ _MAX_TEMPORAL_GROUNDING_ACTIONABLE_SAMPLES = 3
52
+ _MAX_TEMPORAL_GROUNDING_ACTIONABLE_SAMPLE_VALUES = 5
53
+ _MAX_QUERY_ROLE_COVERAGE_ACTIONABLE_SAMPLES = 3
54
+ _MAX_QUERY_ROLE_COVERAGE_ACTIONABLE_SAMPLE_VALUES = 5
55
+
56
+
57
+ def actionable_gap_summary(
58
+ *,
59
+ evaluation_count: int,
60
+ expected_case_count: int,
61
+ failed_gates: Sequence[str],
62
+ query_overlap_count: int,
63
+ profile_overlap_count: int,
64
+ intent_overlap_count: int,
65
+ ref_gate: Mapping[str, object] | None = None,
66
+ bundle_quality_failure_breakdown: Mapping[str, object] | None = None,
67
+ bundle_gap_breakdown: Mapping[str, object] | None = None,
68
+ answerability_gap_breakdown: Mapping[str, object] | None = None,
69
+ selected_evidence_weakness: Mapping[str, object] | None = None,
70
+ evidence_recall_gap_summary: Mapping[str, object] | None = None,
71
+ query_role_gap_breakdown: Mapping[str, object] | None = None,
72
+ query_plan_gap_breakdown: Mapping[str, object] | None = None,
73
+ rerank_signal_gap_breakdown: Mapping[str, object] | None = None,
74
+ temporal_grounding: Mapping[str, object] | None = None,
75
+ source_ref_provenance: Mapping[str, object] | None = None,
76
+ answer_context_provenance: Mapping[str, object] | None = None,
77
+ answer_context_support_gap_summary: Mapping[str, object] | None = None,
78
+ ) -> dict[str, object]:
79
+ failed_gate_set = set(failed_gates)
80
+ ref_gate = _mapping(ref_gate)
81
+ bundle_quality_failure_breakdown = _mapping(bundle_quality_failure_breakdown)
82
+ bundle_gap_breakdown = _mapping(bundle_gap_breakdown)
83
+ answerability_gap_breakdown = _mapping(answerability_gap_breakdown)
84
+ selected_evidence_weakness = _mapping(selected_evidence_weakness)
85
+ evidence_recall_gap_summary = _mapping(evidence_recall_gap_summary)
86
+ query_role_gap_breakdown = _mapping(query_role_gap_breakdown)
87
+ query_plan_gap_breakdown = _mapping(query_plan_gap_breakdown)
88
+ rerank_signal_gap_breakdown = _mapping(rerank_signal_gap_breakdown)
89
+ temporal_grounding = _mapping(temporal_grounding)
90
+ source_ref_provenance = _mapping(source_ref_provenance)
91
+ answer_context_provenance = _mapping(answer_context_provenance)
92
+ answer_context_support_gap_summary = _mapping(answer_context_support_gap_summary)
93
+ gaps: list[dict[str, object]] = []
94
+ _append_bundle_quality_gaps(
95
+ gaps,
96
+ evaluation_count=evaluation_count,
97
+ failed_gate_set=failed_gate_set,
98
+ breakdown=bundle_quality_failure_breakdown,
99
+ )
100
+ _append_counted_gap_map(
101
+ gaps,
102
+ evaluation_count=evaluation_count,
103
+ category="bundle_gap",
104
+ source_metric="bundle_gap_breakdown.reason_counts",
105
+ counts=_count_mapping(bundle_gap_breakdown.get("reason_counts")),
106
+ failed_gate=(
107
+ "evidence_bundle_complete"
108
+ if "evidence_bundle_complete" in failed_gate_set
109
+ else ""
110
+ ),
111
+ action="Improve bundle planning so required evidence roles are selected.",
112
+ samples=_sequence(bundle_gap_breakdown.get("samples")),
113
+ )
114
+ _append_selected_evidence_weakness_gaps(
115
+ gaps,
116
+ evaluation_count=evaluation_count,
117
+ failed_gate_set=failed_gate_set,
118
+ breakdown=selected_evidence_weakness,
119
+ )
120
+ _append_evidence_recall_gaps(
121
+ gaps,
122
+ evaluation_count=evaluation_count,
123
+ summary=evidence_recall_gap_summary,
124
+ )
125
+ _append_answerability_gaps(
126
+ gaps,
127
+ evaluation_count=evaluation_count,
128
+ failed_gate_set=failed_gate_set,
129
+ breakdown=answerability_gap_breakdown,
130
+ )
131
+ _append_query_plan_gaps(
132
+ gaps,
133
+ evaluation_count=evaluation_count,
134
+ failed_gate_set=failed_gate_set,
135
+ breakdown=query_plan_gap_breakdown,
136
+ )
137
+ _append_query_leakage_gaps(
138
+ gaps,
139
+ evaluation_count=evaluation_count,
140
+ failed_gate_set=failed_gate_set,
141
+ query_overlap_count=query_overlap_count,
142
+ profile_overlap_count=profile_overlap_count,
143
+ intent_overlap_count=intent_overlap_count,
144
+ )
145
+ _append_query_role_gaps(
146
+ gaps,
147
+ evaluation_count=evaluation_count,
148
+ breakdown=query_role_gap_breakdown,
149
+ )
150
+ _append_temporal_grounding_gaps(
151
+ gaps,
152
+ evaluation_count=evaluation_count,
153
+ temporal_grounding=temporal_grounding,
154
+ )
155
+ _append_rerank_signal_gaps(
156
+ gaps,
157
+ evaluation_count=evaluation_count,
158
+ breakdown=rerank_signal_gap_breakdown,
159
+ )
160
+ _append_observed_ref_rank_gaps(
161
+ gaps,
162
+ evaluation_count=evaluation_count,
163
+ failed_gate_set=failed_gate_set,
164
+ ref_gate=ref_gate,
165
+ )
166
+ _append_source_ref_gap(
167
+ gaps,
168
+ evaluation_count=evaluation_count,
169
+ source_ref_provenance=source_ref_provenance,
170
+ )
171
+ _append_answer_context_provenance_gap(
172
+ gaps,
173
+ evaluation_count=evaluation_count,
174
+ answer_context_provenance=answer_context_provenance,
175
+ )
176
+ _append_answer_context_support_gaps(
177
+ gaps,
178
+ evaluation_count=evaluation_count,
179
+ summary=answer_context_support_gap_summary,
180
+ )
181
+ ranked = _rank_actionable_gaps(gaps)
182
+ return {
183
+ "schema_version": "actionable_gap_summary.v1",
184
+ "evaluation_count": evaluation_count,
185
+ "expected_case_count": expected_case_count,
186
+ "gap_count": len(ranked),
187
+ "blocking_gap_count": sum(1 for gap in ranked if gap["severity"] == "blocking"),
188
+ "diagnostic_gap_count": sum(
189
+ 1 for gap in ranked if gap["severity"] == "diagnostic"
190
+ ),
191
+ "rank_basis": "observed_impact_desc_blocking_tie_break",
192
+ "top_gap": ranked[0] if ranked else None,
193
+ "ranked_gaps": ranked[:_MAX_RANKED_GAPS],
194
+ }
195
+
196
+
197
+ def _append_bundle_quality_gaps(
198
+ gaps: list[dict[str, object]],
199
+ *,
200
+ evaluation_count: int,
201
+ failed_gate_set: set[str],
202
+ breakdown: Mapping[str, object],
203
+ ) -> None:
204
+ weak_count = _positive_int(breakdown.get("weak_bundle_count")) or 0
205
+ _append_actionable_gap(
206
+ gaps,
207
+ evaluation_count=evaluation_count,
208
+ category="bundle_quality",
209
+ gap="weak_bundle_quality",
210
+ impact_count=weak_count,
211
+ failed_gate=(
212
+ "bundle_quality_medium_or_high"
213
+ if "bundle_quality_medium_or_high" in failed_gate_set
214
+ else ""
215
+ ),
216
+ source_metric="bundle_quality_failure_breakdown.weak_bundle_count",
217
+ action="Raise weak bundles to medium or high quality before expanding evaluation.",
218
+ evidence={
219
+ "risk_reason_counts": _count_mapping(breakdown.get("risk_reason_counts")),
220
+ "top_reason_counts": _count_mapping(breakdown.get("top_reason_counts")),
221
+ },
222
+ samples=_sequence(breakdown.get("weak_samples")),
223
+ )
224
+ _append_counted_gap_map(
225
+ gaps,
226
+ evaluation_count=evaluation_count,
227
+ category="bundle_quality_risk",
228
+ source_metric="bundle_quality_failure_breakdown.risk_reason_counts",
229
+ counts=_count_mapping(breakdown.get("risk_reason_counts")),
230
+ failed_gate=(
231
+ "bundle_quality_medium_or_high"
232
+ if "bundle_quality_medium_or_high" in failed_gate_set
233
+ else ""
234
+ ),
235
+ action="Reduce bundle quality risk reasons in selected evidence.",
236
+ samples=_sequence(breakdown.get("weak_samples")),
237
+ )
238
+
239
+
240
+ def _append_answerability_gaps(
241
+ gaps: list[dict[str, object]],
242
+ *,
243
+ evaluation_count: int,
244
+ failed_gate_set: set[str],
245
+ breakdown: Mapping[str, object],
246
+ ) -> None:
247
+ lifted_counts = _count_mapping(breakdown.get("lifted_reason_counts"))
248
+ _append_counted_gap_map(
249
+ gaps,
250
+ evaluation_count=evaluation_count,
251
+ category="lifted_answerability_gap",
252
+ source_metric="answerability_gap_breakdown.lifted_reason_counts",
253
+ counts=lifted_counts,
254
+ failed_gate=(
255
+ "lifted_answerability_gaps_clear"
256
+ if "lifted_answerability_gaps_clear" in failed_gate_set
257
+ else ""
258
+ ),
259
+ action="Stop lifting candidates that still lack required answerability evidence.",
260
+ samples=_sequence(breakdown.get("samples")),
261
+ )
262
+ residual_counts = {
263
+ reason: count
264
+ for reason, count in _count_mapping(breakdown.get("reason_counts")).items()
265
+ if reason not in lifted_counts
266
+ }
267
+ _append_counted_gap_map(
268
+ gaps,
269
+ evaluation_count=evaluation_count,
270
+ category="answerability_gap",
271
+ source_metric="answerability_gap_breakdown.reason_counts",
272
+ counts=residual_counts,
273
+ failed_gate="",
274
+ action="Add retrieval or rerank signals for missing answerability evidence.",
275
+ samples=_sequence(breakdown.get("samples")),
276
+ )
277
+
278
+
279
+ def _append_selected_evidence_weakness_gaps(
280
+ gaps: list[dict[str, object]],
281
+ *,
282
+ evaluation_count: int,
283
+ failed_gate_set: set[str],
284
+ breakdown: Mapping[str, object],
285
+ ) -> None:
286
+ failed_gate_by_gap = {
287
+ "selected_low_answerability": "selected_low_answerability_clear",
288
+ "selected_weak_source_locality": "selected_weak_source_locality_clear",
289
+ "selected_broad_summary": "selected_broad_summary_clear",
290
+ "selected_conflict_or_stale": "selected_conflict_or_stale_clear",
291
+ }
292
+ for gap, count in _count_mapping(breakdown.get("reason_counts")).items():
293
+ failed_gate = failed_gate_by_gap.get(str(gap), "")
294
+ matched_samples = _samples_for_gap(
295
+ _sequence(breakdown.get("samples")),
296
+ str(gap),
297
+ )
298
+ compact_samples = _compact_selected_evidence_actionable_samples(
299
+ matched_samples
300
+ )
301
+ _append_actionable_gap(
302
+ gaps,
303
+ evaluation_count=evaluation_count,
304
+ category="selected_evidence_weakness",
305
+ gap=str(gap),
306
+ impact_count=count,
307
+ failed_gate=failed_gate if failed_gate in failed_gate_set else "",
308
+ source_metric="selected_evidence_weakness.reason_counts",
309
+ action=_action_for_gap(
310
+ "selected_evidence_weakness",
311
+ str(gap),
312
+ default=(
313
+ "Tighten selected evidence filters for answerability, locality, "
314
+ "and provenance."
315
+ ),
316
+ ),
317
+ samples=matched_samples,
318
+ sample_payloads=compact_samples,
319
+ )
320
+
321
+
322
+ def _append_evidence_recall_gaps(
323
+ gaps: list[dict[str, object]],
324
+ *,
325
+ evaluation_count: int,
326
+ summary: Mapping[str, object],
327
+ ) -> None:
328
+ samples = _compact_evidence_recall_actionable_samples(
329
+ _sequence(summary.get("samples"))
330
+ )
331
+ _append_actionable_gap(
332
+ gaps,
333
+ evaluation_count=evaluation_count,
334
+ category="evidence_recall",
335
+ gap="missing_evidence_refs",
336
+ impact_count=(
337
+ _positive_int(summary.get("missing_evidence_ref_case_count")) or 0
338
+ ),
339
+ source_metric="evidence_recall_gap_summary.missing_evidence_ref_case_count",
340
+ action=(
341
+ "Inspect sampled missing evidence refs and add retrieval coverage "
342
+ "for the absent source turns."
343
+ ),
344
+ evidence={
345
+ "top_missing_evidence_terms": _compact_evidence_recall_count_mapping(
346
+ summary.get("top_missing_evidence_terms")
347
+ ),
348
+ "measured_evidence_recall_count": (
349
+ _positive_int(summary.get("measured_evidence_recall_count")) or 0
350
+ ),
351
+ "avg_evidence_term_recall": _number(
352
+ summary.get("avg_evidence_term_recall")
353
+ ),
354
+ },
355
+ samples=_evidence_recall_samples_for_missing(samples),
356
+ sample_payloads=_evidence_recall_samples_for_missing(samples),
357
+ )
358
+ _append_actionable_gap(
359
+ gaps,
360
+ evaluation_count=evaluation_count,
361
+ category="evidence_recall",
362
+ gap="weak_evidence_term_recall",
363
+ impact_count=(
364
+ _positive_int(summary.get("weak_evidence_recall_case_count")) or 0
365
+ ),
366
+ source_metric="evidence_recall_gap_summary.weak_evidence_recall_case_count",
367
+ action=(
368
+ "Improve retrieval coverage for cases with partial evidence-term "
369
+ "recall before expanding evaluation."
370
+ ),
371
+ evidence={
372
+ "zero_evidence_recall_case_count": (
373
+ _positive_int(summary.get("zero_evidence_recall_case_count")) or 0
374
+ ),
375
+ "measured_evidence_recall_count": (
376
+ _positive_int(summary.get("measured_evidence_recall_count")) or 0
377
+ ),
378
+ "avg_evidence_term_recall": _number(
379
+ summary.get("avg_evidence_term_recall")
380
+ ),
381
+ },
382
+ samples=_evidence_recall_samples_for_weak_recall(samples),
383
+ sample_payloads=_evidence_recall_samples_for_weak_recall(samples),
384
+ )
385
+
386
+
387
+ def _append_query_plan_gaps(
388
+ gaps: list[dict[str, object]],
389
+ *,
390
+ evaluation_count: int,
391
+ failed_gate_set: set[str],
392
+ breakdown: Mapping[str, object],
393
+ ) -> None:
394
+ failed_gate = (
395
+ "query_plan_evidence_roles_clear"
396
+ if "query_plan_evidence_roles_clear" in failed_gate_set
397
+ else ""
398
+ )
399
+ _append_query_plan_missing_role_family_gaps(
400
+ gaps,
401
+ evaluation_count=evaluation_count,
402
+ failed_gate=failed_gate,
403
+ counts=_count_mapping(
404
+ breakdown.get("missing_evidence_role_query_family_counts")
405
+ ),
406
+ details=_mapping(
407
+ breakdown.get("missing_evidence_role_query_family_details")
408
+ ),
409
+ samples=_query_plan_samples(breakdown),
410
+ )
411
+ _append_query_plan_reason_gaps(
412
+ gaps,
413
+ evaluation_count=evaluation_count,
414
+ failed_gate=failed_gate,
415
+ counts=_count_mapping(breakdown.get("gap_reason_counts")),
416
+ samples=_query_plan_samples(breakdown),
417
+ )
418
+
419
+
420
+ def _append_query_plan_missing_role_family_gaps(
421
+ gaps: list[dict[str, object]],
422
+ *,
423
+ evaluation_count: int,
424
+ failed_gate: str,
425
+ counts: Mapping[str, int],
426
+ details: Mapping[str, object],
427
+ samples: Sequence[object],
428
+ ) -> None:
429
+ for family, count in sorted(counts.items()):
430
+ detail = _mapping(details.get(family))
431
+ accepted_families = _str_tuple(detail.get("accepted_query_families"))
432
+ evidence: dict[str, object] = {
433
+ "role_family": str(family),
434
+ "role_family_label": str(
435
+ detail.get("role_family_label") or _role_family_label(str(family))
436
+ ),
437
+ }
438
+ if accepted_families:
439
+ evidence["accepted_query_families"] = list(accepted_families)
440
+ action = str(detail.get("action") or "").strip() or (
441
+ "Add query-plan coverage for required evidence role families."
442
+ )
443
+ matched_samples = _query_plan_samples_for_gap(
444
+ samples,
445
+ str(family),
446
+ key="missing_evidence_role_query_families",
447
+ )
448
+ compact_samples = _compact_query_plan_actionable_samples(
449
+ matched_samples,
450
+ limit=_MAX_SAMPLE_CASE_IDS,
451
+ )
452
+ _append_actionable_gap(
453
+ gaps,
454
+ evaluation_count=evaluation_count,
455
+ category="query_plan",
456
+ gap=str(family),
457
+ impact_count=count,
458
+ failed_gate=failed_gate,
459
+ source_metric=(
460
+ "query_plan_gap_breakdown."
461
+ "missing_evidence_role_query_family_counts"
462
+ ),
463
+ action=action,
464
+ evidence=evidence,
465
+ samples=compact_samples,
466
+ sample_payloads=compact_samples,
467
+ )
468
+
469
+
470
+ def _append_query_plan_reason_gaps(
471
+ gaps: list[dict[str, object]],
472
+ *,
473
+ evaluation_count: int,
474
+ failed_gate: str,
475
+ counts: Mapping[str, int],
476
+ samples: Sequence[object],
477
+ ) -> None:
478
+ for reason, count in sorted(counts.items()):
479
+ matched_samples = _query_plan_samples_for_gap(
480
+ samples,
481
+ str(reason),
482
+ key="gap_reasons",
483
+ )
484
+ compact_samples = _compact_query_plan_actionable_samples(
485
+ matched_samples,
486
+ limit=_MAX_SAMPLE_CASE_IDS,
487
+ )
488
+ _append_actionable_gap(
489
+ gaps,
490
+ evaluation_count=evaluation_count,
491
+ category="query_plan",
492
+ gap=str(reason),
493
+ impact_count=count,
494
+ failed_gate=failed_gate,
495
+ source_metric="query_plan_gap_breakdown.gap_reason_counts",
496
+ action=_query_plan_reason_action(str(reason)),
497
+ samples=compact_samples,
498
+ sample_payloads=compact_samples,
499
+ )
500
+
501
+
502
+ def _append_query_role_gaps(
503
+ gaps: list[dict[str, object]],
504
+ *,
505
+ evaluation_count: int,
506
+ breakdown: Mapping[str, object],
507
+ ) -> None:
508
+ samples = _sequence(breakdown.get("samples"))
509
+ _append_required_query_role_coverage_gaps(
510
+ gaps,
511
+ evaluation_count=evaluation_count,
512
+ breakdown=breakdown,
513
+ )
514
+ for family, payload in _mapping(breakdown.get("role_family_gaps")).items():
515
+ role_gap = _mapping(payload)
516
+ selected_evidence_count = (
517
+ _positive_int(
518
+ role_gap.get("selected_evidence_query_role_family_count")
519
+ )
520
+ or 0
521
+ )
522
+ selected_count = max(
523
+ _positive_int(role_gap.get("selected_item_count")) or 0,
524
+ selected_evidence_count,
525
+ )
526
+ candidate_count = _positive_int(role_gap.get("candidate_count")) or 0
527
+ matched_samples = _query_role_samples_for_family(samples, str(family))
528
+ compact_samples = _compact_query_role_actionable_samples(matched_samples)
529
+ _append_actionable_gap(
530
+ gaps,
531
+ evaluation_count=evaluation_count,
532
+ category="query_role_family",
533
+ gap=str(family),
534
+ impact_count=max(0, candidate_count - selected_count),
535
+ source_metric="query_role_gap_breakdown.role_family_gaps",
536
+ action=(
537
+ "Select evidence for query role families that have candidates "
538
+ "but no bundle coverage."
539
+ ),
540
+ evidence={"gap_reasons": list(_str_tuple(role_gap.get("gap_reasons")))},
541
+ samples=compact_samples,
542
+ sample_payloads=compact_samples,
543
+ )
544
+
545
+
546
+ def _append_required_query_role_coverage_gaps(
547
+ gaps: list[dict[str, object]],
548
+ *,
549
+ evaluation_count: int,
550
+ breakdown: Mapping[str, object],
551
+ ) -> None:
552
+ samples = _sequence(breakdown.get("required_role_coverage_gap_samples"))
553
+ for reason, count in _count_mapping(
554
+ breakdown.get("required_role_coverage_gap_counts")
555
+ ).items():
556
+ matched_samples = _samples_for_gap(samples, str(reason))
557
+ compact_samples = _compact_query_role_coverage_actionable_samples(
558
+ matched_samples
559
+ )
560
+ _append_actionable_gap(
561
+ gaps,
562
+ evaluation_count=evaluation_count,
563
+ category="query_role_coverage",
564
+ gap=str(reason),
565
+ impact_count=count,
566
+ source_metric=(
567
+ "query_role_gap_breakdown.required_role_coverage_gap_counts"
568
+ ),
569
+ action=_query_role_coverage_action(str(reason)),
570
+ evidence={
571
+ "required_roles": _required_roles_from_samples(matched_samples),
572
+ },
573
+ samples=matched_samples,
574
+ sample_payloads=compact_samples,
575
+ )
576
+
577
+
578
+ def _append_temporal_grounding_gaps(
579
+ gaps: list[dict[str, object]],
580
+ *,
581
+ evaluation_count: int,
582
+ temporal_grounding: Mapping[str, object],
583
+ ) -> None:
584
+ reason_counts = _count_mapping(
585
+ temporal_grounding.get("selected_temporal_grounding_issue_reason_counts")
586
+ )
587
+ if not reason_counts:
588
+ return
589
+ samples = _sequence(
590
+ temporal_grounding.get("selected_temporal_grounding_issue_samples")
591
+ )
592
+ for reason, count in sorted(reason_counts.items()):
593
+ matched_samples = _samples_for_gap(samples, str(reason))
594
+ compact_samples = _compact_temporal_grounding_actionable_samples(
595
+ matched_samples
596
+ )
597
+ _append_actionable_gap(
598
+ gaps,
599
+ evaluation_count=evaluation_count,
600
+ category="temporal_grounding",
601
+ gap=str(reason),
602
+ impact_count=count,
603
+ source_metric=(
604
+ "temporal_grounding_table."
605
+ "selected_temporal_grounding_issue_reason_counts"
606
+ ),
607
+ action=_temporal_grounding_action(str(reason)),
608
+ evidence={
609
+ "temporal_case_count": (
610
+ _positive_int(temporal_grounding.get("temporal_case_count")) or 0
611
+ ),
612
+ "selected_temporal_grounding_issue_item_count": (
613
+ _positive_int(
614
+ temporal_grounding.get(
615
+ "selected_temporal_grounding_issue_item_count"
616
+ )
617
+ )
618
+ or 0
619
+ ),
620
+ "selected_strong_temporal_grounding_item_count": (
621
+ _positive_int(
622
+ temporal_grounding.get(
623
+ "selected_strong_temporal_grounding_item_count"
624
+ )
625
+ )
626
+ or 0
627
+ ),
628
+ },
629
+ samples=matched_samples,
630
+ sample_payloads=compact_samples,
631
+ )
632
+
633
+
634
+ def _append_query_leakage_gaps(
635
+ gaps: list[dict[str, object]],
636
+ *,
637
+ evaluation_count: int,
638
+ failed_gate_set: set[str],
639
+ query_overlap_count: int,
640
+ profile_overlap_count: int,
641
+ intent_overlap_count: int,
642
+ ) -> None:
643
+ failed_gate = (
644
+ "query_profile_leakage_zero"
645
+ if "query_profile_leakage_zero" in failed_gate_set
646
+ else ""
647
+ )
648
+ for gap, count in (
649
+ ("expected_answer_query_overlap", query_overlap_count),
650
+ ("expected_answer_query_profile_overlap", profile_overlap_count),
651
+ ("expected_answer_retrieval_intent_overlap", intent_overlap_count),
652
+ ):
653
+ _append_actionable_gap(
654
+ gaps,
655
+ evaluation_count=evaluation_count,
656
+ category="query_leakage",
657
+ gap=gap,
658
+ impact_count=count,
659
+ failed_gate=failed_gate,
660
+ source_metric=f"query_integrity.{gap}_count",
661
+ action=(
662
+ "Remove expected-answer terms from query text, query profile, "
663
+ "and retrieval intent."
664
+ ),
665
+ )
666
+
667
+
668
+ def _append_rerank_signal_gaps(
669
+ gaps: list[dict[str, object]],
670
+ *,
671
+ evaluation_count: int,
672
+ breakdown: Mapping[str, object],
673
+ ) -> None:
674
+ conflict_samples = _compact_rerank_signal_actionable_samples(
675
+ _sequence(breakdown.get("selection_conflict_samples"))
676
+ )
677
+ _append_actionable_gap(
678
+ gaps,
679
+ evaluation_count=evaluation_count,
680
+ category="rerank_signal_selection",
681
+ gap="selection_conflict",
682
+ impact_count=(
683
+ _positive_int(breakdown.get("selection_conflict_case_count")) or 0
684
+ ),
685
+ source_metric="rerank_signal_gap_breakdown.selection_conflict_case_count",
686
+ action=(
687
+ "Inspect cases where positively reranked candidates were not selected "
688
+ "while selected evidence lacked positive rerank support."
689
+ ),
690
+ evidence={
691
+ "selection_conflict_pair_count": (
692
+ _positive_int(breakdown.get("selection_conflict_pair_count")) or 0
693
+ ),
694
+ "positive_unselected_signal_counts": _compact_rerank_signal_mapping(
695
+ _count_mapping(
696
+ breakdown.get("selection_conflict_positive_signal_counts")
697
+ )
698
+ ),
699
+ "selected_without_positive_reason_counts": _compact_rerank_signal_mapping(
700
+ _count_mapping(breakdown.get("selected_without_positive_reason_counts"))
701
+ ),
702
+ },
703
+ samples=conflict_samples,
704
+ sample_payloads=conflict_samples,
705
+ )
706
+
707
+
708
+ def _append_observed_ref_rank_gaps(
709
+ gaps: list[dict[str, object]],
710
+ *,
711
+ evaluation_count: int,
712
+ failed_gate_set: set[str],
713
+ ref_gate: Mapping[str, object],
714
+ ) -> None:
715
+ ref_eval_count = _positive_int(ref_gate.get("evaluation_count")) or 0
716
+ if not ref_eval_count:
717
+ return
718
+ for metric, failed_gate, action in (
719
+ (
720
+ "all_refs_top5_count",
721
+ "all_refs_top5",
722
+ "Move all required evidence refs into the observed top five.",
723
+ ),
724
+ (
725
+ "focused_refs_top5_count",
726
+ "focused_refs_top5",
727
+ "Move focused required evidence refs into the observed top five.",
728
+ ),
729
+ (
730
+ "all_refs_top3_count",
731
+ "all_refs_top3",
732
+ "Move all required evidence refs into the observed top three.",
733
+ ),
734
+ ):
735
+ passing_count = _positive_int(ref_gate.get(metric)) or 0
736
+ _append_actionable_gap(
737
+ gaps,
738
+ evaluation_count=evaluation_count,
739
+ category="evidence_ref_rank",
740
+ gap=metric.removesuffix("_count"),
741
+ impact_count=max(0, ref_eval_count - passing_count),
742
+ failed_gate=failed_gate if failed_gate in failed_gate_set else "",
743
+ source_metric=f"evidence_ref_rank_gate.{metric}",
744
+ action=action,
745
+ evidence={"evidence_ref_evaluation_count": ref_eval_count},
746
+ samples=_sequence(ref_gate.get("samples")),
747
+ )
748
+
749
+
750
+ def _append_source_ref_gap(
751
+ gaps: list[dict[str, object]],
752
+ *,
753
+ evaluation_count: int,
754
+ source_ref_provenance: Mapping[str, object],
755
+ ) -> None:
756
+ samples = _compact_source_ref_actionable_samples(
757
+ _sequence(source_ref_provenance.get("source_refless_selected_samples"))
758
+ )
759
+ _append_actionable_gap(
760
+ gaps,
761
+ evaluation_count=evaluation_count,
762
+ category="source_ref_provenance",
763
+ gap="selected_bundle_missing_source_refs",
764
+ impact_count=(
765
+ _positive_int(
766
+ source_ref_provenance.get("selected_bundle_source_refless_item_count")
767
+ )
768
+ or 0
769
+ ),
770
+ source_metric=(
771
+ "source_ref_provenance.selected_bundle_source_refless_item_count"
772
+ ),
773
+ action="Keep source refs on selected bundle items so evidence remains auditable.",
774
+ evidence={
775
+ "selected_bundle_item_count": (
776
+ _positive_int(source_ref_provenance.get("selected_bundle_item_count"))
777
+ or 0
778
+ ),
779
+ "selected_bundle_source_ref_item_count": (
780
+ _positive_int(
781
+ source_ref_provenance.get("selected_bundle_source_ref_item_count")
782
+ )
783
+ or 0
784
+ ),
785
+ "selected_bundle_source_ref_coverage_rate": _number(
786
+ source_ref_provenance.get("selected_bundle_source_ref_coverage_rate")
787
+ ),
788
+ },
789
+ samples=samples,
790
+ sample_payloads=samples,
791
+ )
792
+
793
+
794
+ def _append_answer_context_provenance_gap(
795
+ gaps: list[dict[str, object]],
796
+ *,
797
+ evaluation_count: int,
798
+ answer_context_provenance: Mapping[str, object],
799
+ ) -> None:
800
+ source_refless_item_count = (
801
+ _positive_int(answer_context_provenance.get("source_refless_item_count"))
802
+ or 0
803
+ )
804
+ source_refless_samples = _compact_answer_context_actionable_samples(
805
+ _sequence(answer_context_provenance.get("source_refless_context_samples"))
806
+ )
807
+ _append_actionable_gap(
808
+ gaps,
809
+ evaluation_count=evaluation_count,
810
+ category="answer_context_provenance",
811
+ gap="answer_context_missing_source_refs",
812
+ impact_count=source_refless_item_count,
813
+ source_metric="answer_context_provenance.source_refless_item_count",
814
+ action=(
815
+ "Keep source refs when selected evidence is rendered into answer "
816
+ "context so prompt evidence remains auditable."
817
+ ),
818
+ evidence={
819
+ "source_refless_context_count": (
820
+ _positive_int(
821
+ answer_context_provenance.get("source_refless_context_count")
822
+ )
823
+ or 0
824
+ ),
825
+ "source_ref_item_coverage_rate": _number(
826
+ answer_context_provenance.get("source_ref_item_coverage_rate")
827
+ ),
828
+ "source_counts": _count_mapping(
829
+ answer_context_provenance.get("source_counts")
830
+ ),
831
+ },
832
+ samples=source_refless_samples,
833
+ sample_payloads=source_refless_samples,
834
+ )
835
+
836
+
837
+ def _append_answer_context_support_gaps(
838
+ gaps: list[dict[str, object]],
839
+ *,
840
+ evaluation_count: int,
841
+ summary: Mapping[str, object],
842
+ ) -> None:
843
+ samples = _sequence(summary.get("samples"))
844
+ support_gap_evidence = {
845
+ "support_gap_context_count": (
846
+ _positive_int(summary.get("support_gap_context_count")) or 0
847
+ ),
848
+ "source_counts": _count_mapping(summary.get("source_counts")),
849
+ }
850
+ availability_samples = _sequence(summary.get("availability_gap_samples"))
851
+ availability_counts = {
852
+ "missing_answer_context": (
853
+ _positive_int(summary.get("missing_answer_context_count")) or 0
854
+ ),
855
+ "unsupported_answer_context": (
856
+ _positive_int(summary.get("unsupported_answer_context_count")) or 0
857
+ ),
858
+ }
859
+ for reason, count in sorted(availability_counts.items()):
860
+ matched_samples = _samples_for_gap(availability_samples, reason)
861
+ compact_samples = _compact_answer_context_actionable_samples(matched_samples)
862
+ _append_actionable_gap(
863
+ gaps,
864
+ evaluation_count=evaluation_count,
865
+ category="answer_context_availability",
866
+ gap=reason,
867
+ impact_count=count,
868
+ source_metric=f"answer_context_support_gap_summary.{reason}_count",
869
+ action=_answer_context_support_action(reason),
870
+ evidence={
871
+ "expected_context_count": (
872
+ _positive_int(summary.get("expected_context_count")) or 0
873
+ ),
874
+ "context_count": _positive_int(summary.get("context_count")) or 0,
875
+ },
876
+ samples=matched_samples,
877
+ sample_payloads=compact_samples,
878
+ )
879
+
880
+ for reason, count in sorted(_count_mapping(summary.get("gap_reason_counts")).items()):
881
+ matched_samples = _samples_for_gap(samples, str(reason))
882
+ compact_samples = _compact_answer_context_actionable_samples(matched_samples)
883
+ _append_actionable_gap(
884
+ gaps,
885
+ evaluation_count=evaluation_count,
886
+ category="answer_context_support",
887
+ gap=str(reason),
888
+ impact_count=count,
889
+ source_metric="answer_context_support_gap_summary.gap_reason_counts",
890
+ action=_answer_context_support_action(str(reason)),
891
+ evidence=support_gap_evidence,
892
+ samples=matched_samples,
893
+ sample_payloads=compact_samples,
894
+ )
895
+
896
+ for reason, count in sorted(
897
+ _count_mapping(summary.get("risk_reason_counts")).items()
898
+ ):
899
+ matched_samples = _samples_for_gap(samples, str(reason))
900
+ compact_samples = _compact_answer_context_actionable_samples(matched_samples)
901
+ _append_actionable_gap(
902
+ gaps,
903
+ evaluation_count=evaluation_count,
904
+ category="answer_context_risk",
905
+ gap=str(reason),
906
+ impact_count=count,
907
+ source_metric="answer_context_support_gap_summary.risk_reason_counts",
908
+ action=_answer_context_risk_action(str(reason)),
909
+ evidence=support_gap_evidence,
910
+ samples=matched_samples,
911
+ sample_payloads=compact_samples,
912
+ )
913
+
914
+ for role, count in sorted(
915
+ _count_mapping(summary.get("missing_required_role_counts")).items()
916
+ ):
917
+ matched_samples = _answer_context_samples_for_missing_role(samples, str(role))
918
+ compact_samples = _compact_answer_context_actionable_samples(matched_samples)
919
+ _append_actionable_gap(
920
+ gaps,
921
+ evaluation_count=evaluation_count,
922
+ category="answer_context_required_role",
923
+ gap=str(role),
924
+ impact_count=count,
925
+ source_metric=(
926
+ "answer_context_support_gap_summary.missing_required_role_counts"
927
+ ),
928
+ action=(
929
+ "Render answer context with selected evidence for the "
930
+ f"{_role_family_label(str(role))} required role."
931
+ ),
932
+ evidence={
933
+ "support_gap_context_count": (
934
+ _positive_int(summary.get("support_gap_context_count")) or 0
935
+ ),
936
+ "role": str(role),
937
+ },
938
+ samples=matched_samples,
939
+ sample_payloads=compact_samples,
940
+ )
941
+
942
+
943
+ def _append_counted_gap_map(
944
+ gaps: list[dict[str, object]],
945
+ *,
946
+ evaluation_count: int,
947
+ category: str,
948
+ source_metric: str,
949
+ counts: Mapping[str, int],
950
+ failed_gate: str,
951
+ action: str,
952
+ samples: Sequence[object] = (),
953
+ ) -> None:
954
+ for gap, count in sorted(counts.items()):
955
+ _append_actionable_gap(
956
+ gaps,
957
+ evaluation_count=evaluation_count,
958
+ category=category,
959
+ gap=str(gap),
960
+ impact_count=count,
961
+ failed_gate=failed_gate,
962
+ source_metric=source_metric,
963
+ action=_action_for_gap(category, str(gap), default=action),
964
+ samples=_samples_for_gap(samples, str(gap)),
965
+ )
966
+
967
+
968
+ def _append_actionable_gap(
969
+ gaps: list[dict[str, object]],
970
+ *,
971
+ evaluation_count: int,
972
+ category: str,
973
+ gap: str,
974
+ impact_count: int,
975
+ source_metric: str,
976
+ action: str,
977
+ failed_gate: str = "",
978
+ evidence: Mapping[str, object] | None = None,
979
+ samples: Sequence[object] = (),
980
+ sample_payloads: Sequence[Mapping[str, object]] = (),
981
+ ) -> None:
982
+ if impact_count <= 0:
983
+ return
984
+ payload: dict[str, object] = {
985
+ "category": category,
986
+ "gap": gap,
987
+ "impact_count": impact_count,
988
+ "impact_rate": _ratio(impact_count, evaluation_count),
989
+ "severity": "blocking" if failed_gate else "diagnostic",
990
+ "failed_gate": failed_gate,
991
+ "source_metric": source_metric,
992
+ "action": action,
993
+ "sample_case_ids": _sample_case_ids(samples),
994
+ "evidence": dict(evidence or {}),
995
+ }
996
+ compact_samples = [
997
+ dict(sample)
998
+ for sample in sample_payloads
999
+ if isinstance(sample, Mapping)
1000
+ ][:3]
1001
+ if compact_samples:
1002
+ payload["samples"] = compact_samples
1003
+ gaps.append(payload)
1004
+
1005
+
1006
+ def _rank_actionable_gaps(
1007
+ gaps: Sequence[Mapping[str, object]],
1008
+ ) -> list[dict[str, object]]:
1009
+ ranked = [
1010
+ dict(gap)
1011
+ for gap in sorted(
1012
+ gaps,
1013
+ key=lambda gap: (
1014
+ -(_positive_int(gap.get("impact_count")) or 0),
1015
+ 0 if gap.get("severity") == "blocking" else 1,
1016
+ _actionable_gap_category_rank(str(gap.get("category") or "")),
1017
+ str(gap.get("category") or ""),
1018
+ str(gap.get("gap") or ""),
1019
+ str(gap.get("source_metric") or ""),
1020
+ ),
1021
+ )
1022
+ ]
1023
+ for index, gap in enumerate(ranked, start=1):
1024
+ gap["rank"] = index
1025
+ return ranked
1026
+
1027
+
1028
+ def _actionable_gap_category_rank(category: str) -> int:
1029
+ return {
1030
+ "answer_context_required_role": 10,
1031
+ "answer_context_support": 11,
1032
+ "answer_context_risk": 12,
1033
+ }.get(category, 100)
1034
+
1035
+
1036
+ def _samples_for_gap(
1037
+ samples: Sequence[object],
1038
+ gap: str,
1039
+ ) -> tuple[Mapping[str, object], ...]:
1040
+ matched = [
1041
+ sample
1042
+ for sample in samples
1043
+ if isinstance(sample, Mapping)
1044
+ and (
1045
+ gap in _str_tuple(sample.get("reasons"))
1046
+ or gap in _str_tuple(sample.get("reason_codes"))
1047
+ or gap in _str_tuple(sample.get("gap_reasons"))
1048
+ or gap in _str_tuple(sample.get("issue_reasons"))
1049
+ or gap in _str_tuple(sample.get("risk_reason_codes"))
1050
+ )
1051
+ ]
1052
+ if matched:
1053
+ return tuple(matched)
1054
+ return tuple(sample for sample in samples if isinstance(sample, Mapping))
1055
+
1056
+
1057
+ def _sample_case_ids(samples: Sequence[object]) -> list[str]:
1058
+ case_ids: list[str] = []
1059
+ for sample in samples:
1060
+ if not isinstance(sample, Mapping):
1061
+ continue
1062
+ case_id = _compact_query_plan_sample_text(sample.get("case_id"))
1063
+ if case_id and case_id not in case_ids:
1064
+ case_ids.append(case_id)
1065
+ if len(case_ids) >= _MAX_SAMPLE_CASE_IDS:
1066
+ break
1067
+ return case_ids
1068
+
1069
+
1070
+ def _number(value: object) -> float:
1071
+ if isinstance(value, bool):
1072
+ return 0.0
1073
+ try:
1074
+ return round(float(value), 6)
1075
+ except (TypeError, ValueError):
1076
+ return 0.0
1077
+
1078
+
1079
+ def _action_for_gap(category: str, gap: str, *, default: str) -> str:
1080
+ if gap.startswith("missing_required_"):
1081
+ role = gap.removeprefix("missing_required_")
1082
+ return f"Ensure required bundle role '{role}' is planned and selected."
1083
+ if gap.startswith("missing_"):
1084
+ need = gap.removeprefix("missing_")
1085
+ return f"Add retrieval and bundle coverage for '{need}'."
1086
+ if category == "selected_evidence_weakness":
1087
+ return (
1088
+ "Reject or demote selected evidence with weak answerability, locality, "
1089
+ "or provenance."
1090
+ )
1091
+ if category == "bundle_quality_risk":
1092
+ return f"Reduce bundle quality risk '{gap}' in selected evidence."
1093
+ return default
1094
+
1095
+
1096
+ def _answer_context_support_action(reason: str) -> str:
1097
+ if reason == "missing_answer_context":
1098
+ return (
1099
+ "Render answer_context diagnostics for every cutoff result in "
1100
+ "compact reports."
1101
+ )
1102
+ if reason == "unsupported_answer_context":
1103
+ return (
1104
+ "Emit answer_context diagnostics as a structured mapping for every "
1105
+ "cutoff result."
1106
+ )
1107
+ if reason in {"missing_context_source_refs", "partial_context_source_refs"}:
1108
+ return "Preserve source refs when selected evidence is rendered into answer context."
1109
+ if reason == "missing_required_roles":
1110
+ return "Render answer context from bundle items that cover required roles before fallback."
1111
+ if reason == "retrieval_slice_fallback":
1112
+ return (
1113
+ "Fix bundle planning or selection so answer context does not fall "
1114
+ "back to retrieval slices."
1115
+ )
1116
+ if reason == "weak_bundle_source_support":
1117
+ return (
1118
+ "Use bundle evidence with direct source-ref or source-identity "
1119
+ "support in answer context."
1120
+ )
1121
+ if reason in {"low_answerability_backfill", "low_context_answerability"}:
1122
+ return "Replace low-answerability answer-context items with stronger selected evidence."
1123
+ if reason in {"weak_source_locality_backfill", "weak_context_source_locality"}:
1124
+ return "Prefer source-local evidence when backfilling answer context."
1125
+ if reason == "skipped_duplicate_source_bundle_item":
1126
+ return (
1127
+ "Diversify selected answer-context bundle sources before duplicate-source "
1128
+ "suppression drops supporting evidence."
1129
+ )
1130
+ if reason == "skipped_noisy_overlap_bundle_item":
1131
+ return (
1132
+ "Select cleaner supporting evidence so noisy-overlap suppression does "
1133
+ "not remove answer-context support."
1134
+ )
1135
+ if reason.startswith("skipped_"):
1136
+ return "Inspect skipped answer-context backfills and add grounded bundle coverage."
1137
+ return f"Resolve answer-context support gap '{reason}'."
1138
+
1139
+
1140
+ def _answer_context_risk_action(reason: str) -> str:
1141
+ return {
1142
+ "risk:retrieval_backfill": (
1143
+ "Replace answer-context retrieval backfill with selected bundle evidence."
1144
+ ),
1145
+ "risk:backfilled_broad_summary": (
1146
+ "Prefer localized source turns over broad summaries when backfilling "
1147
+ "answer context."
1148
+ ),
1149
+ "risk:backfilled_conflict_or_stale": (
1150
+ "Avoid stale or conflicting memories in answer-context backfill."
1151
+ ),
1152
+ "risk:backfilled_low_answerability": (
1153
+ "Backfill answer context with evidence that has stronger measured "
1154
+ "answerability."
1155
+ ),
1156
+ "risk:backfilled_weak_source_locality": (
1157
+ "Backfill answer context with source-local evidence."
1158
+ ),
1159
+ "risk:skipped_target_limit_backfill": (
1160
+ "Increase grounded bundle coverage before answer-context target "
1161
+ "limits suppress backfill."
1162
+ ),
1163
+ }.get(reason, f"Reduce answer-context risk reason '{reason}'.")
1164
+
1165
+
1166
+ def _query_plan_samples(
1167
+ breakdown: Mapping[str, object],
1168
+ ) -> tuple[Mapping[str, object], ...]:
1169
+ raw_samples = tuple(
1170
+ sample
1171
+ for sample in _sequence(breakdown.get("samples"))
1172
+ if isinstance(sample, Mapping)
1173
+ )
1174
+ if raw_samples:
1175
+ return raw_samples
1176
+ compact_samples = tuple(
1177
+ sample
1178
+ for sample in _sequence(breakdown.get("compact_samples"))
1179
+ if isinstance(sample, Mapping)
1180
+ )
1181
+ return compact_samples
1182
+
1183
+
1184
+ def _query_plan_samples_for_gap(
1185
+ samples: Sequence[object],
1186
+ gap: str,
1187
+ *,
1188
+ key: str,
1189
+ ) -> tuple[Mapping[str, object], ...]:
1190
+ matched = [
1191
+ sample
1192
+ for sample in samples
1193
+ if isinstance(sample, Mapping) and gap in _str_tuple(sample.get(key))
1194
+ ]
1195
+ if matched:
1196
+ return tuple(matched)
1197
+ return tuple(sample for sample in samples if isinstance(sample, Mapping))
1198
+
1199
+
1200
+ def _answer_context_samples_for_missing_role(
1201
+ samples: Sequence[object],
1202
+ role: str,
1203
+ ) -> tuple[Mapping[str, object], ...]:
1204
+ matched = [
1205
+ sample
1206
+ for sample in samples
1207
+ if isinstance(sample, Mapping)
1208
+ and role in _str_tuple(sample.get("missing_required_roles"))
1209
+ ]
1210
+ if matched:
1211
+ return tuple(matched)
1212
+ return tuple(sample for sample in samples if isinstance(sample, Mapping))
1213
+
1214
+
1215
+ def _compact_query_plan_actionable_samples(
1216
+ samples: Sequence[Mapping[str, object]],
1217
+ *,
1218
+ limit: int = _MAX_QUERY_PLAN_ACTIONABLE_SAMPLES,
1219
+ ) -> tuple[dict[str, object], ...]:
1220
+ compact_samples: list[dict[str, object]] = []
1221
+ for sample in samples:
1222
+ compact: dict[str, object] = {}
1223
+ for key in ("case_id", "group"):
1224
+ value = _compact_query_plan_sample_text(sample.get(key))
1225
+ if value:
1226
+ compact[key] = value
1227
+ for key in (
1228
+ "gap_reasons",
1229
+ "missing_evidence_role_query_families",
1230
+ "missing_recommended_role_families",
1231
+ "selected_role_families",
1232
+ "required_evidence_roles",
1233
+ "dropped_roles",
1234
+ "dropped_type_limit_roles",
1235
+ "replaced_type_limit_roles",
1236
+ "type_limit_replacement_roles",
1237
+ ):
1238
+ values = tuple(
1239
+ value
1240
+ for value in (
1241
+ _compact_query_plan_sample_text(raw_value)
1242
+ for raw_value in _str_tuple(sample.get(key))
1243
+ )
1244
+ if value
1245
+ )
1246
+ if values:
1247
+ compact[key] = list(values[:_MAX_QUERY_PLAN_ACTIONABLE_SAMPLE_VALUES])
1248
+ for key in (
1249
+ "selected_query_count",
1250
+ "dropped_query_count",
1251
+ "empty_query_candidate_count",
1252
+ ):
1253
+ value = _positive_int(sample.get(key)) or 0
1254
+ if value:
1255
+ compact[key] = value
1256
+ for key in ("fanout_limit_hit", "type_limit_hit"):
1257
+ if sample.get(key) is True:
1258
+ compact[key] = True
1259
+ if compact:
1260
+ compact_samples.append(compact)
1261
+ if len(compact_samples) >= limit:
1262
+ break
1263
+ return tuple(compact_samples)
1264
+
1265
+
1266
+ def _query_role_samples_for_family(
1267
+ samples: Sequence[object],
1268
+ family: str,
1269
+ ) -> tuple[Mapping[str, object], ...]:
1270
+ mapped = tuple(sample for sample in samples if isinstance(sample, Mapping))
1271
+ matched = [
1272
+ sample
1273
+ for sample in mapped
1274
+ if family in _str_tuple(sample.get("query_role_gap_families"))
1275
+ or family in _str_tuple(sample.get("query_role_families"))
1276
+ or family == str(sample.get("query_role") or "").strip()
1277
+ ]
1278
+ return tuple(matched or mapped)
1279
+
1280
+
1281
+ def _compact_query_role_actionable_samples(
1282
+ samples: Sequence[Mapping[str, object]],
1283
+ ) -> tuple[dict[str, object], ...]:
1284
+ compact_samples: list[dict[str, object]] = []
1285
+ for sample in samples:
1286
+ compact: dict[str, object] = {}
1287
+ for key in (
1288
+ "case_id",
1289
+ "group",
1290
+ "query_role",
1291
+ "memory_id",
1292
+ "fusion_score_winner_query_role",
1293
+ "fusion_selected_evidence_query_role",
1294
+ ):
1295
+ value = _compact_query_plan_sample_text(sample.get(key))
1296
+ if value:
1297
+ compact[key] = value
1298
+ for key in (
1299
+ "query_role_families",
1300
+ "query_role_gap_families",
1301
+ "gap_reasons",
1302
+ "positive_signal_names",
1303
+ "policy_reason_codes",
1304
+ "answerability_reason_codes",
1305
+ "source_locality_reason_codes",
1306
+ "fusion_selected_evidence_query_role_families",
1307
+ "fusion_evidence_selection_reason_codes",
1308
+ "selected_bundle_role_families",
1309
+ "selected_bundle_query_role_families",
1310
+ ):
1311
+ values = tuple(
1312
+ value
1313
+ for value in (
1314
+ _compact_query_plan_sample_text(raw_value)
1315
+ for raw_value in _str_tuple(sample.get(key))
1316
+ )
1317
+ if value
1318
+ )
1319
+ if values:
1320
+ compact[key] = list(values[:_MAX_QUERY_ROLE_ACTIONABLE_SAMPLE_VALUES])
1321
+ for key in (
1322
+ "rank",
1323
+ "positive_signal_count",
1324
+ "policy_reason_count",
1325
+ "answerability_reason_count",
1326
+ "source_locality_reason_count",
1327
+ "fusion_evidence_selection_reason_count",
1328
+ "selected_bundle_role_family_count",
1329
+ "selected_bundle_query_role_family_count",
1330
+ ):
1331
+ value = _positive_int(sample.get(key)) or 0
1332
+ if value:
1333
+ compact[key] = value
1334
+ for key in (
1335
+ "positive_policy_score",
1336
+ "answerability_score",
1337
+ "source_locality_score",
1338
+ ):
1339
+ value = sample.get(key)
1340
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
1341
+ compact[key] = round(float(value), 6)
1342
+ for key in ("lifted", "bridge_query_hit"):
1343
+ if sample.get(key) is True:
1344
+ compact[key] = True
1345
+ if compact:
1346
+ compact_samples.append(compact)
1347
+ if len(compact_samples) >= _MAX_QUERY_ROLE_ACTIONABLE_SAMPLES:
1348
+ break
1349
+ return tuple(compact_samples)
1350
+
1351
+
1352
+ def _compact_query_role_coverage_actionable_samples(
1353
+ samples: Sequence[object],
1354
+ ) -> tuple[dict[str, object], ...]:
1355
+ compact_samples: list[dict[str, object]] = []
1356
+ for sample in samples:
1357
+ if not isinstance(sample, Mapping):
1358
+ continue
1359
+ compact: dict[str, object] = {}
1360
+ for key in ("case_id", "group", "required_role", "required_query_family"):
1361
+ value = _compact_query_plan_sample_text(sample.get(key))
1362
+ if value:
1363
+ compact[key] = value
1364
+ for key in (
1365
+ "gap_reasons",
1366
+ "required_selected_query_families",
1367
+ "candidate_query_role_families",
1368
+ "selected_query_role_families",
1369
+ "selected_evidence_query_role_families",
1370
+ ):
1371
+ values = tuple(
1372
+ value
1373
+ for value in (
1374
+ _compact_query_plan_sample_text(raw_value)
1375
+ for raw_value in _str_tuple(sample.get(key))
1376
+ )
1377
+ if value
1378
+ )
1379
+ if values:
1380
+ compact[key] = list(
1381
+ values[:_MAX_QUERY_ROLE_COVERAGE_ACTIONABLE_SAMPLE_VALUES]
1382
+ )
1383
+ if compact:
1384
+ compact_samples.append(compact)
1385
+ if len(compact_samples) >= _MAX_QUERY_ROLE_COVERAGE_ACTIONABLE_SAMPLES:
1386
+ break
1387
+ return tuple(compact_samples)
1388
+
1389
+
1390
+ def _compact_selected_evidence_actionable_samples(
1391
+ samples: Sequence[Mapping[str, object]],
1392
+ ) -> tuple[dict[str, object], ...]:
1393
+ compact_samples: list[dict[str, object]] = []
1394
+ for sample in samples:
1395
+ compact: dict[str, object] = {}
1396
+ for key in ("case_id", "group", "item_id", "role"):
1397
+ value = _compact_query_plan_sample_text(sample.get(key))
1398
+ if value:
1399
+ compact[key] = value
1400
+ for key in (
1401
+ "reasons",
1402
+ "risk_reason_codes",
1403
+ "planner_reason_codes",
1404
+ "query_roles",
1405
+ "answerability_reason_codes",
1406
+ "source_locality_reason_codes",
1407
+ "retrieval_sources",
1408
+ "source_types",
1409
+ ):
1410
+ values = tuple(
1411
+ value
1412
+ for value in (
1413
+ _compact_query_plan_sample_text(raw_value)
1414
+ for raw_value in _str_tuple(sample.get(key))
1415
+ )
1416
+ if value
1417
+ )
1418
+ if values:
1419
+ compact[key] = list(
1420
+ values[:_MAX_SELECTED_EVIDENCE_ACTIONABLE_SAMPLE_VALUES]
1421
+ )
1422
+ raw_source_refs = _str_tuple(sample.get("source_refs"))
1423
+ source_refs = _compact_selected_evidence_source_refs(raw_source_refs)
1424
+ if source_refs:
1425
+ compact["source_refs"] = list(source_refs)
1426
+ source_ref_count = _positive_int(sample.get("source_ref_count")) or 0
1427
+ if not source_ref_count:
1428
+ source_ref_count = (
1429
+ _sanitized_source_ref_count(
1430
+ raw_source_refs,
1431
+ safe_source_refs=source_refs,
1432
+ )
1433
+ or 0
1434
+ )
1435
+ if source_ref_count:
1436
+ compact["source_ref_count"] = source_ref_count
1437
+ for key in ("retrieval_order",):
1438
+ value = _positive_int(sample.get(key)) or 0
1439
+ if value:
1440
+ compact[key] = value
1441
+ for key in ("answerability_score", "source_locality_score"):
1442
+ value = sample.get(key)
1443
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
1444
+ compact[key] = round(float(value), 6)
1445
+ for key in ("broad_summary", "conflict_or_stale"):
1446
+ if sample.get(key) is True:
1447
+ compact[key] = True
1448
+ if compact:
1449
+ compact_samples.append(compact)
1450
+ if len(compact_samples) >= _MAX_SELECTED_EVIDENCE_ACTIONABLE_SAMPLES:
1451
+ break
1452
+ return tuple(compact_samples)
1453
+
1454
+
1455
+ def _compact_selected_evidence_source_refs(value: object) -> tuple[str, ...]:
1456
+ return _compact_diagnostic_source_refs(
1457
+ value,
1458
+ limit=_MAX_SELECTED_EVIDENCE_ACTIONABLE_SAMPLE_VALUES,
1459
+ )
1460
+
1461
+
1462
+ def _compact_source_ref_actionable_samples(
1463
+ samples: Sequence[object],
1464
+ ) -> tuple[dict[str, object], ...]:
1465
+ compact_samples: list[dict[str, object]] = []
1466
+ for raw_sample in samples:
1467
+ sample = _mapping(raw_sample)
1468
+ if not sample:
1469
+ continue
1470
+ compact: dict[str, object] = {}
1471
+ for key in ("case_id", "group", "role"):
1472
+ value = _compact_query_plan_sample_text(sample.get(key))
1473
+ if value:
1474
+ compact[key] = value
1475
+ item_id = _safe_item_id_for_output(sample.get("item_id"))
1476
+ if item_id:
1477
+ compact["item_id"] = item_id
1478
+ retrieval_order = _positive_int(sample.get("retrieval_order")) or 0
1479
+ if retrieval_order:
1480
+ compact["retrieval_order"] = retrieval_order
1481
+ if compact:
1482
+ compact_samples.append(compact)
1483
+ if len(compact_samples) >= _MAX_SELECTED_EVIDENCE_ACTIONABLE_SAMPLES:
1484
+ break
1485
+ return tuple(compact_samples)
1486
+
1487
+
1488
+ def _compact_answer_context_actionable_samples(
1489
+ samples: Sequence[object],
1490
+ ) -> tuple[dict[str, object], ...]:
1491
+ compact_samples: list[dict[str, object]] = []
1492
+ for raw_sample in samples:
1493
+ sample = _mapping(raw_sample)
1494
+ if not sample:
1495
+ continue
1496
+ compact: dict[str, object] = {}
1497
+ for key in ("case_id", "cutoff", "source", "fallback_reason"):
1498
+ value = _compact_query_plan_sample_text(sample.get(key))
1499
+ if value:
1500
+ compact[key] = value
1501
+ for key in (
1502
+ "item_ids",
1503
+ "gap_reasons",
1504
+ "missing_required_roles",
1505
+ "risk_reason_codes",
1506
+ ):
1507
+ values = tuple(
1508
+ value
1509
+ for value in (
1510
+ _compact_query_plan_sample_text(raw_value)
1511
+ for raw_value in _str_tuple(sample.get(key))
1512
+ )
1513
+ if value
1514
+ )
1515
+ if values:
1516
+ compact[key] = list(
1517
+ values[:_MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLE_VALUES]
1518
+ )
1519
+ source_identity_refs = _compact_answer_context_source_identity_refs(
1520
+ sample.get("source_identity_refs")
1521
+ )
1522
+ if source_identity_refs:
1523
+ compact["source_identity_refs"] = list(source_identity_refs)
1524
+ source_identity_items = _compact_answer_context_source_identity_items(
1525
+ sample.get("source_identity_items")
1526
+ )
1527
+ if source_identity_items:
1528
+ compact["source_identity_items"] = list(source_identity_items)
1529
+ retrieval_orders = tuple(
1530
+ order
1531
+ for raw_order in _sequence(sample.get("retrieval_orders"))
1532
+ for order in (_positive_int(raw_order),)
1533
+ if order is not None
1534
+ )
1535
+ if retrieval_orders:
1536
+ compact["retrieval_orders"] = list(
1537
+ retrieval_orders[:_MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLE_VALUES]
1538
+ )
1539
+ for key in (
1540
+ "memory_count",
1541
+ "source_ref_count",
1542
+ "source_ref_item_count",
1543
+ "source_refless_item_count",
1544
+ "source_identity_ref_count",
1545
+ "source_identity_item_count",
1546
+ "backfilled_retrieval_item_count",
1547
+ "skipped_redundant_risky_backfill_count",
1548
+ "skipped_redundant_source_backfill_count",
1549
+ "skipped_redundant_role_backfill_count",
1550
+ "skipped_target_limit_backfill_count",
1551
+ "skipped_duplicate_source_bundle_item_count",
1552
+ "skipped_noisy_overlap_bundle_item_count",
1553
+ ):
1554
+ value = _positive_int(sample.get(key)) or 0
1555
+ if value:
1556
+ compact[key] = value
1557
+ for key in (
1558
+ "avg_measured_answerability_score",
1559
+ "avg_measured_source_locality_score",
1560
+ ):
1561
+ value = sample.get(key)
1562
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
1563
+ compact[key] = round(float(value), 6)
1564
+ if compact:
1565
+ compact_samples.append(compact)
1566
+ if len(compact_samples) >= _MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLES:
1567
+ break
1568
+ return tuple(compact_samples)
1569
+
1570
+
1571
+ def _compact_answer_context_source_identity_refs(value: object) -> tuple[str, ...]:
1572
+ refs = tuple(
1573
+ dict.fromkeys(
1574
+ ref
1575
+ for raw_ref in _str_tuple(value)
1576
+ for ref in (_safe_answer_context_source_identity_ref(raw_ref),)
1577
+ if ref
1578
+ )
1579
+ )
1580
+ return refs[:_MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLE_VALUES]
1581
+
1582
+
1583
+ def _compact_answer_context_source_identity_items(
1584
+ value: object,
1585
+ ) -> tuple[dict[str, object], ...]:
1586
+ compact_items: list[dict[str, object]] = []
1587
+ for raw_item in _sequence(value):
1588
+ item = _mapping(raw_item)
1589
+ if not item:
1590
+ continue
1591
+ compact: dict[str, object] = {}
1592
+ item_id = _safe_item_id_for_output(item.get("item_id"))
1593
+ if item_id:
1594
+ compact["item_id"] = item_id
1595
+ retrieval_order = _positive_int(item.get("retrieval_order"))
1596
+ if retrieval_order is not None:
1597
+ compact["retrieval_order"] = retrieval_order
1598
+ source_identity_refs = _compact_answer_context_source_identity_refs(
1599
+ item.get("source_identity_refs")
1600
+ )
1601
+ if source_identity_refs:
1602
+ compact["source_identity_refs"] = list(source_identity_refs)
1603
+ if compact:
1604
+ compact_items.append(compact)
1605
+ if len(compact_items) >= _MAX_ANSWER_CONTEXT_ACTIONABLE_SAMPLE_VALUES:
1606
+ break
1607
+ return tuple(compact_items)
1608
+
1609
+
1610
+ def _safe_answer_context_source_identity_ref(value: object) -> str | None:
1611
+ return _safe_source_identity_ref(value)
1612
+
1613
+
1614
+ def _compact_evidence_recall_actionable_samples(
1615
+ samples: Sequence[object],
1616
+ ) -> tuple[dict[str, object], ...]:
1617
+ compact_samples: list[dict[str, object]] = []
1618
+ for raw_sample in samples:
1619
+ sample = _mapping(raw_sample)
1620
+ if not sample:
1621
+ continue
1622
+ compact: dict[str, object] = {}
1623
+ for key in ("case_id", "group"):
1624
+ value = _compact_query_plan_sample_text(sample.get(key))
1625
+ if value:
1626
+ compact[key] = value
1627
+ for key in ("expected_term_recall", "evidence_term_recall"):
1628
+ value = sample.get(key)
1629
+ if isinstance(value, (int, float)) and not isinstance(value, bool):
1630
+ compact[key] = round(float(value), 6)
1631
+ if sample.get("evidence_term_recall_measured") is not None:
1632
+ compact["evidence_term_recall_measured"] = (
1633
+ sample.get("evidence_term_recall_measured") is True
1634
+ )
1635
+ missing_terms = tuple(
1636
+ value
1637
+ for value in (
1638
+ _compact_query_plan_sample_text(raw_value)
1639
+ for raw_value in _str_tuple(sample.get("missing_evidence_terms"))
1640
+ )
1641
+ if value
1642
+ )
1643
+ if missing_terms:
1644
+ compact["missing_evidence_terms"] = list(
1645
+ missing_terms[:_MAX_EVIDENCE_RECALL_ACTIONABLE_SAMPLE_VALUES]
1646
+ )
1647
+ missing_count = _positive_int(sample.get("missing_evidence_term_count")) or 0
1648
+ if missing_count:
1649
+ compact["missing_evidence_term_count"] = missing_count
1650
+ if "bundle_complete" in sample:
1651
+ compact["bundle_complete"] = sample.get("bundle_complete") is True
1652
+ if compact:
1653
+ compact_samples.append(compact)
1654
+ if len(compact_samples) >= _MAX_EVIDENCE_RECALL_ACTIONABLE_SAMPLES:
1655
+ break
1656
+ return tuple(compact_samples)
1657
+
1658
+
1659
+ def _compact_evidence_recall_count_mapping(value: object) -> dict[str, int]:
1660
+ compact: dict[str, int] = {}
1661
+ for key, raw_count in _mapping(value).items():
1662
+ compact_key = _compact_query_plan_sample_text(key)
1663
+ if not compact_key:
1664
+ continue
1665
+ count = _positive_int(raw_count) or 0
1666
+ compact[compact_key] = count
1667
+ if len(compact) >= _MAX_EVIDENCE_RECALL_ACTIONABLE_SAMPLE_VALUES:
1668
+ break
1669
+ return compact
1670
+
1671
+
1672
+ def _compact_temporal_grounding_actionable_samples(
1673
+ samples: Sequence[Mapping[str, object]],
1674
+ ) -> tuple[dict[str, object], ...]:
1675
+ compact_samples: list[dict[str, object]] = []
1676
+ for sample in samples:
1677
+ compact: dict[str, object] = {}
1678
+ for key in ("case_id", "group", "item_id", "role"):
1679
+ value = _compact_query_plan_sample_text(sample.get(key))
1680
+ if value:
1681
+ compact[key] = value
1682
+ for key in (
1683
+ "query_roles",
1684
+ "issue_reasons",
1685
+ "source_identity_gap_codes",
1686
+ ):
1687
+ values = tuple(
1688
+ value
1689
+ for value in (
1690
+ _compact_query_plan_sample_text(raw_value)
1691
+ for raw_value in _str_tuple(sample.get(key))
1692
+ )
1693
+ if value
1694
+ )
1695
+ if values:
1696
+ compact[key] = list(
1697
+ values[:_MAX_TEMPORAL_GROUNDING_ACTIONABLE_SAMPLE_VALUES]
1698
+ )
1699
+ raw_source_refs = _str_tuple(sample.get("source_refs"))
1700
+ source_refs = _compact_temporal_grounding_source_refs(raw_source_refs)
1701
+ if source_refs:
1702
+ compact["source_refs"] = list(source_refs)
1703
+ source_ref_count = _positive_int(sample.get("source_ref_count")) or 0
1704
+ if not source_ref_count:
1705
+ source_ref_count = (
1706
+ _sanitized_source_ref_count(
1707
+ raw_source_refs,
1708
+ safe_source_refs=source_refs,
1709
+ )
1710
+ or 0
1711
+ )
1712
+ if source_ref_count:
1713
+ compact["source_ref_count"] = source_ref_count
1714
+ signals = _mapping(sample.get("grounding_signals"))
1715
+ signal_values = {
1716
+ key: bool(signals.get(key))
1717
+ for key in (
1718
+ "source_window",
1719
+ "session_boundary",
1720
+ "date_or_range",
1721
+ "relative_date",
1722
+ "bounded_window",
1723
+ "temporal_order",
1724
+ )
1725
+ if key in signals
1726
+ }
1727
+ if signal_values:
1728
+ compact["grounding_signals"] = signal_values
1729
+ if compact:
1730
+ compact_samples.append(compact)
1731
+ if len(compact_samples) >= _MAX_TEMPORAL_GROUNDING_ACTIONABLE_SAMPLES:
1732
+ break
1733
+ return tuple(compact_samples)
1734
+
1735
+
1736
+ def _compact_temporal_grounding_source_refs(value: object) -> tuple[str, ...]:
1737
+ return _compact_diagnostic_source_refs(
1738
+ value,
1739
+ limit=_MAX_TEMPORAL_GROUNDING_ACTIONABLE_SAMPLE_VALUES,
1740
+ )
1741
+
1742
+
1743
+ def _compact_diagnostic_source_refs(
1744
+ value: object,
1745
+ *,
1746
+ limit: int,
1747
+ ) -> tuple[str, ...]:
1748
+ refs: list[str] = []
1749
+ for raw_ref in _str_tuple(value):
1750
+ for ref in _safe_diagnostic_source_refs_for_value(raw_ref):
1751
+ if ref and ref not in refs:
1752
+ refs.append(ref)
1753
+ if len(refs) >= limit:
1754
+ return tuple(refs)
1755
+ return tuple(refs)
1756
+
1757
+
1758
+ def _safe_diagnostic_source_refs_for_value(value: object) -> tuple[str, ...]:
1759
+ safe_ref = _safe_source_identity_ref(value)
1760
+ if safe_ref:
1761
+ return (safe_ref,)
1762
+ turn_ref = _safe_turn_ref(value)
1763
+ if turn_ref:
1764
+ return (turn_ref,)
1765
+ return tuple(
1766
+ safe_ref
1767
+ for raw_ref in _source_identity_refs_from_source_refs((str(value or ""),))
1768
+ for safe_ref in (_safe_source_identity_ref(raw_ref),)
1769
+ if safe_ref
1770
+ )
1771
+
1772
+
1773
+ def _safe_turn_ref(value: object) -> str:
1774
+ return _safe_exact_turn_ref(value) or ""
1775
+
1776
+
1777
+ def _sanitized_source_ref_count(
1778
+ raw_source_refs: Sequence[str],
1779
+ *,
1780
+ safe_source_refs: Sequence[str],
1781
+ ) -> int | None:
1782
+ if raw_source_refs and tuple(raw_source_refs) != tuple(safe_source_refs):
1783
+ return len(tuple(dict.fromkeys(raw_source_refs)))
1784
+ return None
1785
+
1786
+
1787
+ def _evidence_recall_samples_for_missing(
1788
+ samples: Sequence[Mapping[str, object]],
1789
+ ) -> tuple[Mapping[str, object], ...]:
1790
+ matched = [
1791
+ sample
1792
+ for sample in samples
1793
+ if (_positive_int(sample.get("missing_evidence_term_count")) or 0) > 0
1794
+ ]
1795
+ return tuple(matched or samples)
1796
+
1797
+
1798
+ def _evidence_recall_samples_for_weak_recall(
1799
+ samples: Sequence[Mapping[str, object]],
1800
+ ) -> tuple[Mapping[str, object], ...]:
1801
+ matched = []
1802
+ for sample in samples:
1803
+ if sample.get("evidence_term_recall_measured") is not True:
1804
+ continue
1805
+ value = sample.get("evidence_term_recall")
1806
+ if isinstance(value, bool):
1807
+ continue
1808
+ try:
1809
+ if float(value) < 1.0:
1810
+ matched.append(sample)
1811
+ except (TypeError, ValueError):
1812
+ continue
1813
+ return tuple(matched or samples)
1814
+
1815
+
1816
+ def _compact_rerank_signal_actionable_samples(
1817
+ samples: Sequence[object],
1818
+ ) -> tuple[dict[str, object], ...]:
1819
+ compact_samples: list[dict[str, object]] = []
1820
+ for raw_sample in samples:
1821
+ sample = _mapping(raw_sample)
1822
+ if not sample:
1823
+ continue
1824
+ compact: dict[str, object] = {}
1825
+ for key in ("case_id", "group"):
1826
+ value = _compact_query_plan_sample_text(sample.get(key))
1827
+ if value:
1828
+ compact[key] = value
1829
+ for key in (
1830
+ "positive_unselected_candidate_count",
1831
+ "selected_without_positive_rerank_count",
1832
+ ):
1833
+ value = _positive_int(sample.get(key)) or 0
1834
+ if value:
1835
+ compact[key] = value
1836
+ signal_counts = _compact_rerank_signal_mapping(
1837
+ _mapping(sample.get("positive_unselected_signal_counts"))
1838
+ )
1839
+ if signal_counts:
1840
+ compact["positive_unselected_signal_counts"] = signal_counts
1841
+ candidate_ids = _compact_rerank_signal_item_ids(
1842
+ _sequence(sample.get("positive_unselected_candidates"))
1843
+ )
1844
+ if candidate_ids:
1845
+ compact["positive_unselected_candidate_ids"] = candidate_ids
1846
+ selected_items = _compact_rerank_signal_selected_samples(
1847
+ _sequence(sample.get("selected_without_positive_items"))
1848
+ )
1849
+ if selected_items:
1850
+ compact["selected_without_positive_items"] = selected_items
1851
+ if compact:
1852
+ compact_samples.append(compact)
1853
+ if len(compact_samples) >= _MAX_RERANK_SIGNAL_ACTIONABLE_SAMPLES:
1854
+ break
1855
+ return tuple(compact_samples)
1856
+
1857
+
1858
+ def _compact_rerank_signal_item_ids(
1859
+ samples: Sequence[object],
1860
+ ) -> list[str]:
1861
+ item_ids: list[str] = []
1862
+ for raw_sample in samples:
1863
+ sample = _mapping(raw_sample)
1864
+ if not sample:
1865
+ continue
1866
+ item_id = _safe_item_id_for_output(sample.get("item_id"))
1867
+ if item_id:
1868
+ item_id = _compact_query_plan_sample_text(item_id)
1869
+ if item_id:
1870
+ item_ids.append(item_id)
1871
+ if len(item_ids) >= _MAX_RERANK_SIGNAL_ACTIONABLE_SAMPLE_VALUES:
1872
+ break
1873
+ return item_ids
1874
+
1875
+
1876
+ def _compact_rerank_signal_selected_samples(
1877
+ samples: Sequence[object],
1878
+ ) -> list[dict[str, object]]:
1879
+ compact_samples: list[dict[str, object]] = []
1880
+ for raw_sample in samples:
1881
+ sample = _mapping(raw_sample)
1882
+ if not sample:
1883
+ continue
1884
+ compact: dict[str, object] = {}
1885
+ item_id = _safe_item_id_for_output(sample.get("item_id"))
1886
+ if item_id:
1887
+ compact["item_id"] = _compact_query_plan_sample_text(item_id)
1888
+ for key in ("reason", "role"):
1889
+ value = _compact_query_plan_sample_text(sample.get(key))
1890
+ if value:
1891
+ compact[key] = value
1892
+ if "matched_retrieval_candidate" in sample:
1893
+ compact["matched_retrieval_candidate"] = (
1894
+ sample.get("matched_retrieval_candidate") is True
1895
+ )
1896
+ if compact:
1897
+ compact_samples.append(compact)
1898
+ if len(compact_samples) >= _MAX_RERANK_SIGNAL_ACTIONABLE_SAMPLE_VALUES:
1899
+ break
1900
+ return compact_samples
1901
+
1902
+
1903
+ def _compact_rerank_signal_mapping(
1904
+ values: Mapping[str, object],
1905
+ ) -> dict[str, object]:
1906
+ compact: dict[str, object] = {}
1907
+ for key, raw_value in sorted(values.items()):
1908
+ compact_key = _compact_query_plan_sample_text(key)
1909
+ if not compact_key:
1910
+ continue
1911
+ if isinstance(raw_value, bool):
1912
+ if raw_value:
1913
+ compact[compact_key] = True
1914
+ elif isinstance(raw_value, int):
1915
+ compact[compact_key] = raw_value
1916
+ elif isinstance(raw_value, float):
1917
+ compact[compact_key] = round(float(raw_value), 6)
1918
+ else:
1919
+ compact_value = _compact_query_plan_sample_text(raw_value)
1920
+ if compact_value:
1921
+ compact[compact_key] = compact_value
1922
+ if len(compact) >= _MAX_RERANK_SIGNAL_ACTIONABLE_SAMPLE_VALUES:
1923
+ break
1924
+ return compact
1925
+
1926
+
1927
+ def _compact_query_plan_sample_text(value: object) -> str:
1928
+ text = str(value or "").strip()
1929
+ if len(text) <= _MAX_QUERY_PLAN_ACTIONABLE_SAMPLE_TEXT:
1930
+ return text
1931
+ return f"{text[: _MAX_QUERY_PLAN_ACTIONABLE_SAMPLE_TEXT - 3]}..."
1932
+
1933
+
1934
+ def _required_roles_from_samples(samples: Sequence[object]) -> list[str]:
1935
+ roles: list[str] = []
1936
+ for sample in samples:
1937
+ if not isinstance(sample, Mapping):
1938
+ continue
1939
+ role = _compact_query_plan_sample_text(sample.get("required_role"))
1940
+ if role and role not in roles:
1941
+ roles.append(role)
1942
+ if len(roles) >= _MAX_QUERY_ROLE_COVERAGE_ACTIONABLE_SAMPLE_VALUES:
1943
+ break
1944
+ return roles
1945
+
1946
+
1947
+ def _query_role_coverage_action(reason: str) -> str:
1948
+ return {
1949
+ "candidate_query": (
1950
+ "Add candidate query families for required evidence roles before "
1951
+ "rerank and bundle selection."
1952
+ ),
1953
+ "selected_query": (
1954
+ "Keep selected query families aligned with required evidence roles."
1955
+ ),
1956
+ "selected_evidence_query": (
1957
+ "Tag selected evidence with the query families that satisfy required "
1958
+ "evidence roles."
1959
+ ),
1960
+ "missing_required_evidence": (
1961
+ "Select evidence that satisfies each required bundle role."
1962
+ ),
1963
+ }.get(reason, "Close required evidence role coverage gaps in query planning.")
1964
+
1965
+
1966
+ def _query_plan_reason_action(reason: str) -> str:
1967
+ return {
1968
+ "missing_evidence_role_query_family": (
1969
+ "Add selected query families that satisfy required evidence role "
1970
+ "families."
1971
+ ),
1972
+ "missing_recommended_role_family": (
1973
+ "Add selected queries for recommended role families reported by "
1974
+ "the query plan."
1975
+ ),
1976
+ "dropped_queries": (
1977
+ "Preserve or replace dropped query roles before query-plan fanout "
1978
+ "removes required coverage."
1979
+ ),
1980
+ "fanout_limit_hit": (
1981
+ "Rebalance query-plan fanout so required role families survive "
1982
+ "selection."
1983
+ ),
1984
+ "type_limit_hit": (
1985
+ "Rebalance per-type query limits so semantic and lexical slots keep "
1986
+ "required role-family coverage."
1987
+ ),
1988
+ "type_limit_replacement": (
1989
+ "Verify type-limit replacements keep the required role-family "
1990
+ "coverage they replaced."
1991
+ ),
1992
+ "empty_query_candidate": (
1993
+ "Suppress empty query candidates or backfill valid query text before "
1994
+ "selection."
1995
+ ),
1996
+ }.get(reason, "Fix query-plan gaps that drop required retrieval routes.")
1997
+
1998
+
1999
+ def _temporal_grounding_action(reason: str) -> str:
2000
+ return {
2001
+ "missing_source_window": (
2002
+ "Keep session/turn source-window refs on temporal evidence."
2003
+ ),
2004
+ "missing_session_boundary": (
2005
+ "Preserve session or turn boundaries for selected temporal evidence."
2006
+ ),
2007
+ "missing_date_or_range": (
2008
+ "Preserve explicit or relative date/range evidence on selected "
2009
+ "temporal items."
2010
+ ),
2011
+ "missing_temporal_grounding": (
2012
+ "Add temporal evidence with source windows plus explicit or relative "
2013
+ "date/range grounding."
2014
+ ),
2015
+ "weak_source_window_without_date_or_range": (
2016
+ "Pair temporal source windows with explicit or relative date/range text."
2017
+ ),
2018
+ "weak_session_boundary_without_date_or_range": (
2019
+ "Pair session-bound temporal evidence with explicit or relative "
2020
+ "date/range text."
2021
+ ),
2022
+ "weak_date_or_range_without_session_boundary": (
2023
+ "Pair date/range temporal evidence with session or turn boundaries."
2024
+ ),
2025
+ "source_identity_mismatch": (
2026
+ "Fix selected temporal evidence whose text and source identity disagree."
2027
+ ),
2028
+ "conflicting_or_stale": (
2029
+ "Avoid stale or conflicting selected evidence for temporal answers."
2030
+ ),
2031
+ "weak_broad_summary": (
2032
+ "Prefer localized temporal turns over broad summary evidence."
2033
+ ),
2034
+ }.get(reason, "Fix temporal grounding issues in selected evidence.")
2035
+
2036
+
2037
+ def _role_family_label(family: str) -> str:
2038
+ return family.strip().replace("_", " ")