typedef-data-intelligence 0.35.0__tar.gz → 0.37.0__tar.gz

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 (263) hide show
  1. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/CHANGELOG.md +19 -0
  2. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/PKG-INFO +1 -1
  3. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/pyproject.toml +1 -1
  4. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/app.py +69 -15
  5. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_quickstart.py +10 -4
  6. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_semdiff_provision.py +13 -8
  7. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_sync.py +11 -4
  8. typedef_data_intelligence-0.37.0/src/lineage/observability/__init__.py +1 -0
  9. typedef_data_intelligence-0.37.0/src/lineage/observability/desktop_logfire.py +97 -0
  10. typedef_data_intelligence-0.37.0/src/lineage/observability/local_otlp_export.py +109 -0
  11. typedef_data_intelligence-0.37.0/src/lineage/observability/local_span_export.py +93 -0
  12. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/typedef_cli.py +10 -4
  13. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/git.py +23 -5
  14. typedef_data_intelligence-0.37.0/tests/api/test_app_logfire.py +279 -0
  15. typedef_data_intelligence-0.37.0/tests/api/test_app_retention_wiring.py +20 -0
  16. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_diagnostics.py +202 -3
  17. typedef_data_intelligence-0.37.0/tests/api/test_local_otlp_export.py +72 -0
  18. typedef_data_intelligence-0.37.0/tests/observability/test_no_raw_stream_filehandler.py +36 -0
  19. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_git_utils.py +17 -5
  20. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.dockerignore +0 -0
  21. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.env.example +0 -0
  22. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.gitignore +0 -0
  23. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/.justfile_help.txt +0 -0
  24. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/Dockerfile +0 -0
  25. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/Dockerfile.dev +0 -0
  26. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/Justfile +0 -0
  27. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/README.pypi.md +0 -0
  28. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.api.yml +0 -0
  29. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.cli.yml +0 -0
  30. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.desktop.yml +0 -0
  31. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.ingest.unified.yml +0 -0
  32. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/config.ingest.yml +0 -0
  33. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docker-entrypoint.dev.sh +0 -0
  34. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/LINEAR_TICKETING_BACKEND.md +0 -0
  35. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/BUSINESS_CONCEPT_INDEX.md +0 -0
  36. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/CLUSTERING_ALGORITHM_DECISION.md +0 -0
  37. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/GENERIC_REPORT_SCHEMA.md +0 -0
  38. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/HYBRID_ANALYSIS_ARCHITECTURE.md +0 -0
  39. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/PHASE2_IR_STITCHING.md +0 -0
  40. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/PIPELINE_PRIMITIVES_USECASES.md +0 -0
  41. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/SALESFORCE_REPORT_IR.md +0 -0
  42. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/SEMANTIC_JOIN_RESOLUTION.md +0 -0
  43. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/architecture/SEMANTIC_SCHEMA_DESIGN.md +0 -0
  44. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/AGENT_DESIGN_NOTES.md +0 -0
  45. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/COLUMN_PROFILING.md +0 -0
  46. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/COLUMN_PROFILING_IMPLEMENTATION.md +0 -0
  47. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/EXTRACTION_DETECTION.md +0 -0
  48. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/PERFORMANCE_AND_QUALITY_SPECIALISTS.md +0 -0
  49. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/design/STREAMING_SUBAGENT_ARCHITECTURE.md +0 -0
  50. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/examples/memory_usage_examples.py +0 -0
  51. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/examples/pydantic_tool_customization_example.py +0 -0
  52. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/AGENT_USER_GUIDE.md +0 -0
  53. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/GETTING_STARTED.md +0 -0
  54. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/MIGRATION_THREE_SQL.md +0 -0
  55. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/SCOPE_LEVEL_LINEAGE_GUIDE.md +0 -0
  56. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/SEMANTIC_DIFF_HARNESS.md +0 -0
  57. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/SEMANTIC_DIMENSIONS_GUIDE.md +0 -0
  58. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/docs/reference/TYPE_ENUMS.md +0 -0
  59. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/pytest.ini +0 -0
  60. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/create_salesforce_reports.py +0 -0
  61. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/data_generation/utils.py +0 -0
  62. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/databricks-smoke.py +0 -0
  63. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/delete_salesforce_reports.py +0 -0
  64. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/populate_salesforce_schema.py +0 -0
  65. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/reset_demo_git_workspace.py +0 -0
  66. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/reset_demo_linear.py +0 -0
  67. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/snowflake_server.py +0 -0
  68. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-engine-bench.sh +0 -0
  69. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-fts-b2-corpus.sh +0 -0
  70. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-fts-bench.sh +0 -0
  71. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/scripts/surreal-fts-quality.sh +0 -0
  72. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/snowflake-mcp-config.yaml +0 -0
  73. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/__init__.py +0 -0
  74. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/auth_context.py +0 -0
  75. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/concept_cards.py +0 -0
  76. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/controlplane_identity.py +0 -0
  77. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_agents.py +0 -0
  78. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_context.py +0 -0
  79. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_logs.py +0 -0
  80. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_runtime.py +0 -0
  81. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_setup.py +0 -0
  82. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_wizard_sync.py +0 -0
  83. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_workspaces.py +0 -0
  84. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/desktop_worktree.py +0 -0
  85. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/graph_availability.py +0 -0
  86. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/posthog_analytics.py +0 -0
  87. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/semantic_runtime.py +0 -0
  88. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/api/serve.py +0 -0
  89. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/context_projects/__init__.py +0 -0
  90. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/context_projects/airflow_discovery.py +0 -0
  91. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/context_projects/dbt_plugin.py +0 -0
  92. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/__init__.py +0 -0
  93. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/backward_dryrun.py +0 -0
  94. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/clustering_metrics.py +0 -0
  95. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/drop_snowflake_tables.py +0 -0
  96. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/ephemeral_lineage_report.py +0 -0
  97. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/eval_clustering.py +0 -0
  98. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/list_ephemeral_models.py +0 -0
  99. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/refingerprint_cache.py +0 -0
  100. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/regen_semantic_goldens.py +0 -0
  101. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/reset_git_workspace.py +0 -0
  102. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/reset_linear_workspace.py +0 -0
  103. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/reset_project.py +0 -0
  104. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/run_dbt_workflow.py +0 -0
  105. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/seed_census.py +0 -0
  106. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/semantic_goldens.py +0 -0
  107. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/__init__.py +0 -0
  108. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_bench.py +0 -0
  109. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_cells.py +0 -0
  110. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_corpus.py +0 -0
  111. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_corpus_dump.py +0 -0
  112. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_coverage.py +0 -0
  113. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_coverage_report.py +0 -0
  114. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_oracle.py +0 -0
  115. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_quality.py +0 -0
  116. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/fts_quality_bench.py +0 -0
  117. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/log_parse.py +0 -0
  118. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/report.py +0 -0
  119. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/surreal_bench/runner.py +0 -0
  120. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/__init__.py +0 -0
  121. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/README.md +0 -0
  122. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/__init__.py +0 -0
  123. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/cli.py +0 -0
  124. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/db_names.py +0 -0
  125. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/dbt_compile.py +0 -0
  126. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/diff_runner.py +0 -0
  127. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/expected_changes.py +0 -0
  128. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/fixture.yaml +0 -0
  129. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/mutation.diff +0 -0
  130. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/fixture.yaml +0 -0
  131. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/mutation.diff +0 -0
  132. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/fixture.yaml +0 -0
  133. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/mutation.diff +0 -0
  134. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/fixture.yaml +0 -0
  135. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/mutation.diff +0 -0
  136. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/fixture.yaml +0 -0
  137. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/mutation.diff +0 -0
  138. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/fixture.yaml +0 -0
  139. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/mutation.diff +0 -0
  140. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures_loader.py +0 -0
  141. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/ingest.py +0 -0
  142. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/lifecycle.py +0 -0
  143. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/semantic_diff/source_prep.py +0 -0
  144. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/surreal/__init__.py +0 -0
  145. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/surreal/desktop_mcp.py +0 -0
  146. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/devtools/unified_graph/unified_cli.py +0 -0
  147. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/mcp/__init__.py +0 -0
  148. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/mcp/runner.py +0 -0
  149. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/mcp/server.py +0 -0
  150. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/templates/__init__.py +0 -0
  151. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/templates/typedef_config.yaml.j2 +0 -0
  152. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/__init__.py +0 -0
  153. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/databricks.py +0 -0
  154. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/databricks_oauth.py +0 -0
  155. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/dbt.py +0 -0
  156. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/dbt_profile.py +0 -0
  157. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/env.py +0 -0
  158. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/gitignore.py +0 -0
  159. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/logging.py +0 -0
  160. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/src/lineage/utils/snowflake.py +0 -0
  161. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/__init__.py +0 -0
  162. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_app_lifespan.py +0 -0
  163. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_concept_cards.py +0 -0
  164. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_databricks_auth_wiring.py +0 -0
  165. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_agents_clone_backend.py +0 -0
  166. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_agents_runtime.py +0 -0
  167. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_agents_ticket_resolution.py +0 -0
  168. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_airflow_snapshot.py +0 -0
  169. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_clerk_auth.py +0 -0
  170. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_context_reader_cache.py +0 -0
  171. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databases_delete.py +0 -0
  172. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databases_edit.py +0 -0
  173. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databricks_oauth.py +0 -0
  174. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_databricks_setup.py +0 -0
  175. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_delete_endpoints.py +0 -0
  176. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_logs.py +0 -0
  177. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_orphan_flags.py +0 -0
  178. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_posthog.py +0 -0
  179. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_quickstart.py +0 -0
  180. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_repo_projects_discovery.py +0 -0
  181. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_runtime.py +0 -0
  182. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_salesforce.py +0 -0
  183. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_save_env_credentials.py +0 -0
  184. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_search.py +0 -0
  185. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_secondary_roles_hook.py +0 -0
  186. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_semdiff_provision.py +0 -0
  187. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_setup.py +0 -0
  188. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_setup_clone.py +0 -0
  189. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_setup_env_vars.py +0 -0
  190. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_sync.py +0 -0
  191. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_sync_render_profile.py +0 -0
  192. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_telemetry.py +0 -0
  193. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces.py +0 -0
  194. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_adopt.py +0 -0
  195. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_context_kinds.py +0 -0
  196. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_delete.py +0 -0
  197. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_gold_triggers.py +0 -0
  198. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_integrations.py +0 -0
  199. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_preview_name.py +0 -0
  200. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_projects_and_repos.py +0 -0
  201. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_r3_r10.py +0 -0
  202. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_summary.py +0 -0
  203. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_target_scope.py +0 -0
  204. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_workspaces_wizard.py +0 -0
  205. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree.py +0 -0
  206. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_clone.py +0 -0
  207. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_clone_token_scope.py +0 -0
  208. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_gold_queue.py +0 -0
  209. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_kind_target.py +0 -0
  210. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_desktop_worktree_resolution_scope.py +0 -0
  211. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_graph_availability_any_sentinel.py +0 -0
  212. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_no_legacy_surface.py +0 -0
  213. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_surreal_graph_availability.py +0 -0
  214. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_target_envelope_reads.py +0 -0
  215. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_validate_project_collisions.py +0 -0
  216. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_wizard_create_project_integration_persistence.py +0 -0
  217. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_workspace_clone_settings.py +0 -0
  218. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/api/test_worktree_registry_workspaces_shape.py +0 -0
  219. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/__init__.py +0 -0
  220. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/lineage/__init__.py +0 -0
  221. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/lineage/test_search_enum_coverage.py +0 -0
  222. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/backends/lineage/test_surreal_search.py +0 -0
  223. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/conftest.py +0 -0
  224. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/__init__.py +0 -0
  225. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/__init__.py +0 -0
  226. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_fts_cells.py +0 -0
  227. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_fts_coverage.py +0 -0
  228. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_fts_quality.py +0 -0
  229. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_log_parse.py +0 -0
  230. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/surreal_bench/test_report.py +0 -0
  231. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/test_typedef_home_provision.py +0 -0
  232. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/devtools/test_worktree_reclaim.py +0 -0
  233. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/docs/test_desktop_posthog_event_docs.py +0 -0
  234. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/integration/__init__.py +0 -0
  235. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/integration/test_semantic_diff_harness.py +0 -0
  236. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/__init__.py +0 -0
  237. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/test_desktop_mcp_retry.py +0 -0
  238. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/test_list_projects.py +0 -0
  239. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/mcp/test_server.py +0 -0
  240. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_gitignore_utils.py +0 -0
  241. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_helpers_semantic.py +0 -0
  242. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_model_tier_validation.py +0 -0
  243. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_template_yaml_escaping.py +0 -0
  244. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_typedef_cli_import_budget.py +0 -0
  245. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_unified_cli_airflow.py +0 -0
  246. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_unified_cli_fast.py +0 -0
  247. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/test_unified_cli_semantic.py +0 -0
  248. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/context_projects/test_airflow_discovery.py +0 -0
  249. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/context_projects/test_dbt_plugin.py +0 -0
  250. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/__init__.py +0 -0
  251. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_expected_changes.py +0 -0
  252. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_harness_units.py +0 -0
  253. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_lifecycle.py +0 -0
  254. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_lifecycle_io.py +0 -0
  255. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/devtools/test_semdiff_lifecycle_provisioning.py +0 -0
  256. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/formula/__init__.py +0 -0
  257. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/__init__.py +0 -0
  258. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_databricks_utils.py +0 -0
  259. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_dbt_docs_generate.py +0 -0
  260. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_dbt_profile.py +0 -0
  261. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_logging.py +0 -0
  262. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_run_dbt_command.py +0 -0
  263. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.37.0}/tests/unit/utils/test_snowflake_utils.py +0 -0
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.37.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.36.0...typedef-data-intelligence-v0.37.0) (2026-07-08)
4
+
5
+
6
+ ### Features
7
+
8
+ * **observability:** retention & rotation policy for local logs/traces (TD-3813) ([#1123](https://github.com/typedef-ai/typedef/issues/1123)) ([48dc274](https://github.com/typedef-ai/typedef/commit/48dc274d2e8182a0a2417abf3ba4028278cf8939))
9
+
10
+ ## [0.36.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.35.0...typedef-data-intelligence-v0.36.0) (2026-07-07)
11
+
12
+
13
+ ### Features
14
+
15
+ * **observability:** enable local desktop span export (TD-3805) ([#1114](https://github.com/typedef-ai/typedef/issues/1114)) ([32cf196](https://github.com/typedef-ai/typedef/commit/32cf196272f658a639ef3d6ae9ce3599c79078eb))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **observability:** classify + clean customer data in retained/exported logs (TD-3825) ([#1107](https://github.com/typedef-ai/typedef/issues/1107)) ([cac553d](https://github.com/typedef-ai/typedef/commit/cac553d50a84275a734d198baca25a7290c6ab70))
21
+
3
22
  ## [0.35.0](https://github.com/typedef-ai/typedef/compare/typedef-data-intelligence-v0.34.0...typedef-data-intelligence-v0.35.0) (2026-07-06)
4
23
 
5
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typedef-data-intelligence
3
- Version: 0.35.0
3
+ Version: 0.37.0
4
4
  Summary: Unified lineage management and AI-powered analytics platform for dbt projects
5
5
  Project-URL: Homepage, https://typedef.ai
6
6
  Project-URL: Repository, https://github.com/typedef-ai/data-intelligence
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "typedef-data-intelligence"
3
- version = "0.35.0"
3
+ version = "0.37.0"
4
4
  description = "Unified lineage management and AI-powered analytics platform for dbt projects"
5
5
  readme = "README.pypi.md"
6
6
  license = "LicenseRef-Proprietary"
@@ -6,9 +6,8 @@ import sys
6
6
  from contextlib import asynccontextmanager
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
- from typing import Any, Optional
9
+ from typing import TYPE_CHECKING, Any, Optional
10
10
 
11
- import logfire
12
11
  import yaml
13
12
  from agent.api import shutdown_event
14
13
  from agent.api.profiles import (
@@ -37,6 +36,10 @@ from core.backends.unified_graph.surreal.reader import SurrealUnifiedGraphReader
37
36
  from fastapi import FastAPI, HTTPException, Request
38
37
  from fastapi.middleware.cors import CORSMiddleware
39
38
  from lineage.api.auth_context import resolve_user_context
39
+ from lineage.observability.desktop_logfire import (
40
+ configure_desktop_logfire,
41
+ shutdown_desktop_logfire,
42
+ )
40
43
  from lineage.utils.env import load_env_file
41
44
  from pydantic import ValidationError
42
45
 
@@ -250,6 +253,34 @@ class AppState:
250
253
  unified_reader: Optional[SurrealUnifiedGraphReader] = None
251
254
 
252
255
 
256
+ # ============================================================================
257
+ # Observability Retention (TD-3813)
258
+ # ============================================================================
259
+
260
+ if TYPE_CHECKING:
261
+ from core.observability.retention import RetentionPolicy
262
+
263
+
264
+ async def _periodic_retention_sweep(policy: "RetentionPolicy") -> None:
265
+ """Run the observability retention sweep periodically, off the event loop.
266
+
267
+ Sleeps ``policy.periodic_interval_seconds`` between sweeps. Best-effort;
268
+ only cancellation stops it.
269
+ """
270
+ import asyncio
271
+
272
+ from core.observability.retention import sweep_observability
273
+
274
+ while True:
275
+ try:
276
+ await asyncio.sleep(policy.periodic_interval_seconds)
277
+ await asyncio.to_thread(sweep_observability, policy)
278
+ except asyncio.CancelledError:
279
+ raise
280
+ except Exception:
281
+ logger.debug("periodic observability retention sweep failed", exc_info=True)
282
+
283
+
253
284
  # ============================================================================
254
285
  # Middleware for Request Logging and User Context Extraction
255
286
  # ============================================================================
@@ -305,10 +336,7 @@ async def lifespan(app: FastAPI):
305
336
  logger.debug("gold-build temp dir cleanup failed", exc_info=True)
306
337
 
307
338
  try:
308
- from core.paths import (
309
- cleanup_stale_frontend_logs,
310
- cleanup_stale_wizard_scratch,
311
- )
339
+ from core.paths import cleanup_stale_wizard_scratch
312
340
 
313
341
  removed_wizard_scratch = cleanup_stale_wizard_scratch()
314
342
  if removed_wizard_scratch:
@@ -317,13 +345,25 @@ async def lifespan(app: FastAPI):
317
345
  len(removed_wizard_scratch),
318
346
  )
319
347
 
320
- # TD-3711: prune captured frontend (webview) logs by age + total size
321
- # so optional capture can never grow unbounded.
322
- removed_frontend_logs = cleanup_stale_frontend_logs()
323
- if removed_frontend_logs:
348
+ # TD-3813: one unified age+size retention sweep over the whole logs/
349
+ # tree (traces, index, ops, streams tails, frontend), then a periodic
350
+ # task so an always-on session can't grow unbounded between restarts.
351
+ import asyncio
352
+
353
+ from core.observability.retention import RetentionPolicy, sweep_observability
354
+
355
+ _obs_policy = RetentionPolicy.from_env()
356
+ swept = sweep_observability(_obs_policy)
357
+ _swept_total = sum(len(v) for v in swept.values())
358
+ if _swept_total:
324
359
  logger.info(
325
- "Removed %d stale frontend log file(s) on startup",
326
- len(removed_frontend_logs),
360
+ "Observability retention: removed %d unit(s) on startup (%s)",
361
+ _swept_total,
362
+ ", ".join(f"{k}={len(v)}" for k, v in swept.items() if v),
363
+ )
364
+ if not _obs_policy.disabled:
365
+ app.state._obs_retention_task = asyncio.create_task(
366
+ _periodic_retention_sweep(_obs_policy)
327
367
  )
328
368
 
329
369
  try:
@@ -396,9 +436,9 @@ async def lifespan(app: FastAPI):
396
436
  # If user opts out via the consent modal or Settings, takes effect on next launch.
397
437
  from lineage.api.desktop_setup import _read_telemetry_prefs
398
438
 
399
- logfire_token = os.getenv("LOGFIRE_TOKEN")
400
- if logfire_token and not _read_telemetry_prefs().get("telemetry_opted_out"):
401
- logfire.configure(token=logfire_token, console=False)
439
+ configure_desktop_logfire(
440
+ telemetry_opted_out=bool(_read_telemetry_prefs().get("telemetry_opted_out"))
441
+ )
402
442
 
403
443
  # Startup: no maintained API route initializes the legacy state path.
404
444
  if _NEEDS_LEGACY_STATE:
@@ -746,6 +786,19 @@ async def lifespan(app: FastAPI):
746
786
  "Failed to terminate active gold workers", exc_info=True
747
787
  )
748
788
 
789
+ _obs_task = getattr(app.state, "_obs_retention_task", None)
790
+ if _obs_task is not None:
791
+ import asyncio
792
+
793
+ _obs_task.cancel()
794
+ try:
795
+ await _obs_task
796
+ except (asyncio.CancelledError, Exception):
797
+ # await of a just-cancelled task re-raises CancelledError (a BaseException,
798
+ # NOT Exception in 3.12), so both must be caught; do not simplify to bare
799
+ # except Exception.
800
+ pass
801
+
749
802
  try:
750
803
  from core.observability.manifest import finalize_open_ops
751
804
 
@@ -759,6 +812,7 @@ async def lifespan(app: FastAPI):
759
812
  shutdown_shared_posthog_client()
760
813
  except Exception:
761
814
  logger.debug("Failed to shut down PostHog analytics client", exc_info=True)
815
+ shutdown_desktop_logfire()
762
816
 
763
817
  # Close standalone data backend (e.g. DuckDB file handle)
764
818
  standalone_db = getattr(app.state, "standalone_data_backend", None)
@@ -743,13 +743,19 @@ def _quickstart_worker(
743
743
  "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
744
744
  datefmt="%Y-%m-%d %H:%M:%S",
745
745
  )
746
- _fh = _logging.FileHandler(str(_log_file), mode="a")
747
- _fh.setLevel(_logging.INFO)
748
- _fh.setFormatter(_fmt)
746
+ from core.observability.retention import (
747
+ RetentionPolicy,
748
+ attach_rotating_stream_handler,
749
+ )
749
750
 
750
751
  root = _logging.getLogger()
751
752
  root.setLevel(_logging.INFO)
752
- root.addHandler(_fh)
753
+ attach_rotating_stream_handler(
754
+ root,
755
+ _log_file,
756
+ max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
757
+ formatter=_fmt,
758
+ )
753
759
  from lineage.utils.logging import quiet_sync_dependency_loggers
754
760
 
755
761
  quiet_sync_dependency_loggers()
@@ -78,17 +78,22 @@ def _attach_diff_log_handler() -> None:
78
78
  (``type_inference.*``, ``ingest.*``, ``core.backends.unified_graph.*``)
79
79
  propagates to the child's root and lands in ``diff.log``.
80
80
  """
81
- handler = logging.FileHandler(str(_diff_log_path()), mode="a")
82
- handler.setLevel(logging.INFO)
83
- handler.setFormatter(
84
- logging.Formatter(
85
- "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
86
- datefmt="%Y-%m-%d %H:%M:%S",
87
- )
81
+ from core.observability.retention import (
82
+ RetentionPolicy,
83
+ attach_rotating_stream_handler,
88
84
  )
85
+
89
86
  root = logging.getLogger()
90
87
  root.setLevel(logging.INFO)
91
- root.addHandler(handler)
88
+ attach_rotating_stream_handler(
89
+ root,
90
+ _diff_log_path(),
91
+ max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
92
+ formatter=logging.Formatter(
93
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
94
+ datefmt="%Y-%m-%d %H:%M:%S",
95
+ ),
96
+ )
92
97
 
93
98
 
94
99
  def _load_env() -> None:
@@ -2873,12 +2873,19 @@ def _sync_worker(
2873
2873
  "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
2874
2874
  datefmt="%Y-%m-%d %H:%M:%S",
2875
2875
  )
2876
- _fh = _logging.FileHandler(str(_logs / "sync.log"), mode="a")
2877
- _fh.setLevel(_logging.INFO)
2878
- _fh.setFormatter(_fmt)
2876
+ from core.observability.retention import (
2877
+ RetentionPolicy,
2878
+ attach_rotating_stream_handler,
2879
+ )
2880
+
2879
2881
  root = _logging.getLogger()
2880
2882
  root.setLevel(_logging.INFO)
2881
- root.addHandler(_fh)
2883
+ attach_rotating_stream_handler(
2884
+ root,
2885
+ _logs / "sync.log",
2886
+ max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
2887
+ formatter=_fmt,
2888
+ )
2882
2889
 
2883
2890
  from lineage.utils.logging import quiet_sync_dependency_loggers
2884
2891
 
@@ -0,0 +1 @@
1
+ """Local observability helpers for the desktop backend."""
@@ -0,0 +1,97 @@
1
+ """Desktop Logfire setup with local trace export processors."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ import os
7
+ from typing import Final
8
+
9
+ import logfire
10
+ from opentelemetry.sdk.trace import SpanProcessor
11
+
12
+ from lineage.observability.local_otlp_export import make_local_otlp_span_processor
13
+ from lineage.observability.local_span_export import make_local_span_processor
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ _OTEL_EXPORTER_ENV_KEYS: Final = (
18
+ "OTEL_TRACES_EXPORTER",
19
+ "OTEL_METRICS_EXPORTER",
20
+ "OTEL_LOGS_EXPORTER",
21
+ )
22
+ _OTEL_REMOTE_EXPORT_ENV_KEYS: Final = (
23
+ *_OTEL_EXPORTER_ENV_KEYS,
24
+ "OTEL_EXPORTER_OTLP_ENDPOINT",
25
+ "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
26
+ "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT",
27
+ "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
28
+ )
29
+ _local_only_previous_otel_exporters: dict[str, str | None] | None = None
30
+
31
+
32
+ def configure_desktop_logfire(*, telemetry_opted_out: bool) -> None:
33
+ """Configure cloud-gated Logfire and local desktop span processors."""
34
+ logfire_token = os.getenv("LOGFIRE_TOKEN") or None
35
+ span_processors = _desktop_span_processors()
36
+ send_to_logfire = logfire_token is not None and not telemetry_opted_out
37
+ if send_to_logfire:
38
+ logfire.configure(
39
+ token=logfire_token,
40
+ send_to_logfire=True,
41
+ console=False,
42
+ additional_span_processors=span_processors,
43
+ )
44
+ return
45
+
46
+ _disable_otel_exporters_for_local_only()
47
+ logfire.configure(
48
+ token=None,
49
+ send_to_logfire=False,
50
+ metrics=False,
51
+ console=False,
52
+ additional_span_processors=span_processors,
53
+ )
54
+
55
+
56
+ def shutdown_desktop_logfire() -> None:
57
+ """Flush configured Logfire processors and restore local-only OTEL env changes."""
58
+ try:
59
+ logfire.shutdown()
60
+ except Exception:
61
+ logger.debug("Failed to shut down Logfire", exc_info=True)
62
+ finally:
63
+ _restore_local_only_otel_exporters()
64
+
65
+
66
+ def _desktop_span_processors() -> list[SpanProcessor]:
67
+ processors: list[SpanProcessor] = [make_local_span_processor()]
68
+ local_otlp_processor = make_local_otlp_span_processor()
69
+ if local_otlp_processor is not None:
70
+ processors.append(local_otlp_processor)
71
+ return processors
72
+
73
+
74
+ def _disable_otel_exporters_for_local_only() -> None:
75
+ global _local_only_previous_otel_exporters
76
+ if _local_only_previous_otel_exporters is not None:
77
+ _restore_local_only_otel_exporters()
78
+ if not any(os.getenv(key) for key in _OTEL_REMOTE_EXPORT_ENV_KEYS):
79
+ return
80
+ _local_only_previous_otel_exporters = {
81
+ key: os.environ.get(key) for key in _OTEL_EXPORTER_ENV_KEYS
82
+ }
83
+ for key in _OTEL_EXPORTER_ENV_KEYS:
84
+ os.environ[key] = "none"
85
+
86
+
87
+ def _restore_local_only_otel_exporters() -> None:
88
+ global _local_only_previous_otel_exporters
89
+ previous_exporters = _local_only_previous_otel_exporters
90
+ if previous_exporters is None:
91
+ return
92
+ for key, value in previous_exporters.items():
93
+ if value is None:
94
+ os.environ.pop(key, None)
95
+ else:
96
+ os.environ[key] = value
97
+ _local_only_previous_otel_exporters = None
@@ -0,0 +1,109 @@
1
+ """Local OTLP/HTTP export for explicitly enabled desktop development traces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ipaddress
6
+ import os
7
+ from typing import Final
8
+ from urllib.parse import SplitResult, urlsplit, urlunsplit
9
+
10
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
11
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
12
+
13
+ _LOCAL_OTLP_EXPORT_ENV: Final = "TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT"
14
+ _GENERIC_ENDPOINT_ENV: Final = "OTEL_EXPORTER_OTLP_ENDPOINT"
15
+ _TRACES_ENDPOINT_ENV: Final = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"
16
+ _GENERIC_PROTOCOL_ENV: Final = "OTEL_EXPORTER_OTLP_PROTOCOL"
17
+ _TRACES_PROTOCOL_ENV: Final = "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL"
18
+ _DEFAULT_TRACES_ENDPOINT: Final = "http://127.0.0.1:4318/v1/traces"
19
+ _TRACES_PATH: Final = "/v1/traces"
20
+ _HTTP_PROTOBUF_PROTOCOL: Final = "http/protobuf"
21
+ _ENABLED_VALUES: Final = frozenset({"1", "true", "yes", "on"})
22
+ _LOOPBACK_HOSTNAMES: Final = frozenset({"localhost"})
23
+ _EXPORT_TIMEOUT_SECONDS: Final = 2.0
24
+
25
+
26
+ def resolve_local_otlp_traces_endpoint() -> str | None:
27
+ """Resolve a loopback OTLP/HTTP traces endpoint from development env config."""
28
+ if not _local_otlp_export_enabled():
29
+ return None
30
+ if _resolved_protocol() != _HTTP_PROTOBUF_PROTOCOL:
31
+ return None
32
+ traces_endpoint = _clean_env(_TRACES_ENDPOINT_ENV)
33
+ if traces_endpoint is not None:
34
+ return _validated_loopback_endpoint(traces_endpoint)
35
+ generic_endpoint = _clean_env(_GENERIC_ENDPOINT_ENV)
36
+ if generic_endpoint is None:
37
+ return _DEFAULT_TRACES_ENDPOINT
38
+ return _validated_loopback_endpoint(_with_traces_path(generic_endpoint))
39
+
40
+
41
+ def make_local_otlp_span_processor() -> BatchSpanProcessor | None:
42
+ """Create a local OTLP/HTTP span processor when development export is enabled."""
43
+ endpoint = resolve_local_otlp_traces_endpoint()
44
+ if endpoint is None:
45
+ return None
46
+ return BatchSpanProcessor(
47
+ OTLPSpanExporter(endpoint=endpoint, timeout=_EXPORT_TIMEOUT_SECONDS)
48
+ )
49
+
50
+
51
+ def _local_otlp_export_enabled() -> bool:
52
+ value = _clean_env(_LOCAL_OTLP_EXPORT_ENV)
53
+ return value is not None and value.lower() in _ENABLED_VALUES
54
+
55
+
56
+ def _resolved_protocol() -> str:
57
+ traces_protocol = _clean_env(_TRACES_PROTOCOL_ENV)
58
+ if traces_protocol is not None:
59
+ return traces_protocol.lower()
60
+ generic_protocol = _clean_env(_GENERIC_PROTOCOL_ENV)
61
+ if generic_protocol is not None:
62
+ return generic_protocol.lower()
63
+ return _HTTP_PROTOBUF_PROTOCOL
64
+
65
+
66
+ def _clean_env(key: str) -> str | None:
67
+ value = os.getenv(key)
68
+ if value is None:
69
+ return None
70
+ clean_value = value.strip()
71
+ if clean_value:
72
+ return clean_value
73
+ return None
74
+
75
+
76
+ def _with_traces_path(endpoint: str) -> str:
77
+ parsed = urlsplit(endpoint)
78
+ clean_path = parsed.path.rstrip("/")
79
+ if clean_path.endswith(_TRACES_PATH):
80
+ return endpoint
81
+ traces_path = f"{clean_path}{_TRACES_PATH}" if clean_path else _TRACES_PATH
82
+ return urlunsplit(
83
+ SplitResult(
84
+ scheme=parsed.scheme,
85
+ netloc=parsed.netloc,
86
+ path=traces_path,
87
+ query=parsed.query,
88
+ fragment=parsed.fragment,
89
+ )
90
+ )
91
+
92
+
93
+ def _validated_loopback_endpoint(endpoint: str) -> str | None:
94
+ parsed = urlsplit(endpoint)
95
+ if parsed.scheme not in {"http", "https"}:
96
+ return None
97
+ if parsed.username is not None or parsed.password is not None:
98
+ return None
99
+ if parsed.query or parsed.fragment:
100
+ return None
101
+ hostname = parsed.hostname
102
+ if hostname is None:
103
+ return None
104
+ if hostname.lower() in _LOOPBACK_HOSTNAMES:
105
+ return endpoint
106
+ try:
107
+ return endpoint if ipaddress.ip_address(hostname).is_loopback else None
108
+ except ValueError:
109
+ return None
@@ -0,0 +1,93 @@
1
+ """OpenTelemetry span export for local desktop traces."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import threading
7
+ import time
8
+ from collections.abc import Sequence
9
+ from pathlib import Path
10
+ from typing import Final
11
+
12
+ from core.paths import logs_dir
13
+ from opentelemetry.sdk.trace.export import (
14
+ BatchSpanProcessor,
15
+ ReadableSpan,
16
+ SpanExporter,
17
+ SpanExportResult,
18
+ )
19
+
20
+ _TRACE_FILE_PREFIX: Final = "spans"
21
+ _DEFAULT_TRACE_CHUNK_BYTES: Final = 64 * 1024 * 1024
22
+
23
+
24
+ class NdjsonFileSpanExporter(SpanExporter):
25
+ """Write OpenTelemetry spans to per-process NDJSON chunks, rolling to a new chunk.
26
+
27
+ Chunks rotate when the current one crosses ``max_file_bytes`` so a long-lived
28
+ process (the backend) never grows one unbounded file (TD-3813 blocker #2).
29
+ """
30
+
31
+ def __init__(
32
+ self, traces_dir: Path, max_file_bytes: int = _DEFAULT_TRACE_CHUNK_BYTES
33
+ ) -> None:
34
+ """Create an exporter rooted at the configured traces directory."""
35
+ self._traces_dir = traces_dir
36
+ self._pid = os.getpid()
37
+ # per-instance uniqueness key: (pid, start_ns, seq) names each chunk; one
38
+ # exporter per process, so start_ns never collides across restarts.
39
+ self._start_ns = time.time_ns()
40
+ self._seq = 0
41
+ self._max_file_bytes = max_file_bytes
42
+ self._path = self._chunk_path()
43
+ self._lock = threading.Lock()
44
+ self._closed = False
45
+
46
+ def _chunk_path(self) -> Path:
47
+ return (
48
+ self._traces_dir
49
+ / f"{_TRACE_FILE_PREFIX}-{self._pid}-{self._start_ns}-{self._seq:04d}.ndjson"
50
+ )
51
+
52
+ def _maybe_rotate(self) -> None:
53
+ try:
54
+ if self._path.exists() and self._path.stat().st_size >= self._max_file_bytes:
55
+ self._seq += 1
56
+ self._path = self._chunk_path()
57
+ except OSError:
58
+ pass
59
+
60
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
61
+ """Append spans as one JSON object per line, rolling chunks by size."""
62
+ with self._lock:
63
+ if self._closed:
64
+ return SpanExportResult.FAILURE
65
+ try:
66
+ self._traces_dir.mkdir(parents=True, exist_ok=True)
67
+ self._maybe_rotate()
68
+ with self._path.open("a", encoding="utf-8") as trace_file:
69
+ for span in spans:
70
+ trace_file.write(span.to_json(indent=None))
71
+ trace_file.write("\n")
72
+ except (OSError, TypeError, ValueError):
73
+ return SpanExportResult.FAILURE
74
+ return SpanExportResult.SUCCESS
75
+
76
+ def shutdown(self) -> None:
77
+ """Stop accepting new spans."""
78
+ with self._lock:
79
+ self._closed = True
80
+
81
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
82
+ """Report that file writes are already flushed."""
83
+ return True
84
+
85
+
86
+ def make_local_span_processor() -> BatchSpanProcessor:
87
+ """Create the always-on local span processor for desktop startup."""
88
+ from core.observability.retention import RetentionPolicy
89
+
90
+ chunk_bytes = RetentionPolicy.from_env().stream_head_max_bytes
91
+ return BatchSpanProcessor(
92
+ NdjsonFileSpanExporter(logs_dir() / "traces", max_file_bytes=chunk_bytes)
93
+ )
@@ -461,13 +461,19 @@ def sync(projects: tuple[str, ...], all: bool, full: bool, verbose: bool):
461
461
  log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
462
462
  log_datefmt = "%Y-%m-%d %H:%M:%S"
463
463
 
464
- file_handler = logging.FileHandler(str(log_file), mode="a")
465
- file_handler.setLevel(logging.INFO)
466
- file_handler.setFormatter(logging.Formatter(log_format, datefmt=log_datefmt))
464
+ from core.observability.retention import (
465
+ RetentionPolicy,
466
+ attach_rotating_stream_handler,
467
+ )
467
468
 
468
469
  root_logger = logging.getLogger()
469
470
  root_logger.setLevel(logging.INFO)
470
- root_logger.addHandler(file_handler)
471
+ attach_rotating_stream_handler(
472
+ root_logger,
473
+ log_file,
474
+ max_head_bytes=RetentionPolicy.from_env().stream_head_max_bytes,
475
+ formatter=logging.Formatter(log_format, datefmt=log_datefmt),
476
+ )
471
477
 
472
478
  if verbose:
473
479
  console_handler = logging.StreamHandler()
@@ -10,6 +10,8 @@ from collections.abc import AsyncIterator
10
10
  from dataclasses import dataclass
11
11
  from pathlib import Path
12
12
 
13
+ from core.models.observability import sanitize_diagnostic_text
14
+
13
15
  logger = logging.getLogger(__name__)
14
16
 
15
17
 
@@ -69,7 +71,6 @@ class CloneProgress:
69
71
  # Regex to parse git progress lines like:
70
72
  # "Receiving objects: 67% (835/1247)"
71
73
  _PROGRESS_RE = re.compile(r"(Counting|Compressing|Receiving|Resolving)[^:]*:\s*(\d+)%")
72
- _URL_CREDENTIALS_RE = re.compile(r"(?P<scheme>https?://)(?:[^/\s@]+(?::[^/\s@]*)?@)")
73
74
 
74
75
 
75
76
  def _handle_clone_stderr_line(
@@ -91,19 +92,36 @@ def _handle_clone_stderr_line(
91
92
 
92
93
 
93
94
  def _build_clone_failure_message(stderr_lines: list[str], exit_code: int) -> str:
94
- """Return the cleaned clone failure text to surface to callers."""
95
+ """Return the clone failure text to surface to callers.
96
+
97
+ Credential shapes are stripped here (TD-3825) so every downstream consumer
98
+ inherits clean text: the raised ``RuntimeError``, the error ``CloneProgress``
99
+ event, the ``logger.debug`` full-stderr line, and anything that logs the
100
+ exception into ``sync.log`` / ``quickstart.log`` / ``backend-stdout.log``.
101
+ A token-bearing remote URL (``https://TOKEN@host/repo.git``) never reaches
102
+ disk from this path.
103
+ """
95
104
  stderr_detail = "\n".join(line.strip() for line in stderr_lines if line.strip())
96
- return stderr_detail or f"git clone failed (exit {exit_code})"
105
+ if not stderr_detail:
106
+ return f"git clone failed (exit {exit_code})"
107
+ sanitized, _ = sanitize_diagnostic_text(stderr_detail)
108
+ return sanitized
97
109
 
98
110
 
99
111
  def _build_clone_failure_log_summary(stderr_message: str, exit_code: int) -> str:
100
- """Return a redacted one-line summary safe for error logs."""
112
+ """Return a one-line summary safe for error logs.
113
+
114
+ ``stderr_message`` is already credential-stripped by
115
+ :func:`_build_clone_failure_message`; this re-runs the sanitizer defensively
116
+ in case a caller passes raw text.
117
+ """
101
118
  first_line = next(
102
119
  (line.strip() for line in stderr_message.splitlines() if line.strip()), ""
103
120
  )
104
121
  if not first_line:
105
122
  return f"git clone failed (exit {exit_code})"
106
- return _URL_CREDENTIALS_RE.sub(r"\g<scheme>", first_line)
123
+ sanitized, _ = sanitize_diagnostic_text(first_line)
124
+ return sanitized
107
125
 
108
126
 
109
127
  async def clone_repo_with_progress(