typedef-data-intelligence 0.35.0__tar.gz → 0.36.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 (261) hide show
  1. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/CHANGELOG.md +12 -0
  2. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/PKG-INFO +1 -1
  3. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/pyproject.toml +1 -1
  4. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/app.py +8 -4
  5. typedef_data_intelligence-0.36.0/src/lineage/observability/__init__.py +1 -0
  6. typedef_data_intelligence-0.36.0/src/lineage/observability/desktop_logfire.py +97 -0
  7. typedef_data_intelligence-0.36.0/src/lineage/observability/local_otlp_export.py +109 -0
  8. typedef_data_intelligence-0.36.0/src/lineage/observability/local_span_export.py +62 -0
  9. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/git.py +23 -5
  10. typedef_data_intelligence-0.36.0/tests/api/test_app_logfire.py +257 -0
  11. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_diagnostics.py +202 -3
  12. typedef_data_intelligence-0.36.0/tests/api/test_local_otlp_export.py +72 -0
  13. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_git_utils.py +17 -5
  14. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.dockerignore +0 -0
  15. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.env.example +0 -0
  16. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.gitignore +0 -0
  17. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/.justfile_help.txt +0 -0
  18. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/Dockerfile +0 -0
  19. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/Dockerfile.dev +0 -0
  20. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/Justfile +0 -0
  21. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/README.pypi.md +0 -0
  22. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.api.yml +0 -0
  23. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.cli.yml +0 -0
  24. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.desktop.yml +0 -0
  25. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.ingest.unified.yml +0 -0
  26. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/config.ingest.yml +0 -0
  27. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docker-entrypoint.dev.sh +0 -0
  28. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/LINEAR_TICKETING_BACKEND.md +0 -0
  29. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/BUSINESS_CONCEPT_INDEX.md +0 -0
  30. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/CLUSTERING_ALGORITHM_DECISION.md +0 -0
  31. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/GENERIC_REPORT_SCHEMA.md +0 -0
  32. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/HYBRID_ANALYSIS_ARCHITECTURE.md +0 -0
  33. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/PHASE2_IR_STITCHING.md +0 -0
  34. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/PIPELINE_PRIMITIVES_USECASES.md +0 -0
  35. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/SALESFORCE_REPORT_IR.md +0 -0
  36. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/SEMANTIC_JOIN_RESOLUTION.md +0 -0
  37. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/architecture/SEMANTIC_SCHEMA_DESIGN.md +0 -0
  38. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/AGENT_DESIGN_NOTES.md +0 -0
  39. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/COLUMN_PROFILING.md +0 -0
  40. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/COLUMN_PROFILING_IMPLEMENTATION.md +0 -0
  41. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/EXTRACTION_DETECTION.md +0 -0
  42. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/PERFORMANCE_AND_QUALITY_SPECIALISTS.md +0 -0
  43. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/design/STREAMING_SUBAGENT_ARCHITECTURE.md +0 -0
  44. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/examples/memory_usage_examples.py +0 -0
  45. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/examples/pydantic_tool_customization_example.py +0 -0
  46. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/AGENT_USER_GUIDE.md +0 -0
  47. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/GETTING_STARTED.md +0 -0
  48. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/MIGRATION_THREE_SQL.md +0 -0
  49. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/SCOPE_LEVEL_LINEAGE_GUIDE.md +0 -0
  50. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/SEMANTIC_DIFF_HARNESS.md +0 -0
  51. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/SEMANTIC_DIMENSIONS_GUIDE.md +0 -0
  52. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/docs/reference/TYPE_ENUMS.md +0 -0
  53. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/pytest.ini +0 -0
  54. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/create_salesforce_reports.py +0 -0
  55. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/data_generation/utils.py +0 -0
  56. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/databricks-smoke.py +0 -0
  57. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/delete_salesforce_reports.py +0 -0
  58. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/populate_salesforce_schema.py +0 -0
  59. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/reset_demo_git_workspace.py +0 -0
  60. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/reset_demo_linear.py +0 -0
  61. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/snowflake_server.py +0 -0
  62. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-engine-bench.sh +0 -0
  63. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-fts-b2-corpus.sh +0 -0
  64. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-fts-bench.sh +0 -0
  65. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/scripts/surreal-fts-quality.sh +0 -0
  66. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/snowflake-mcp-config.yaml +0 -0
  67. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/__init__.py +0 -0
  68. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/auth_context.py +0 -0
  69. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/concept_cards.py +0 -0
  70. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/controlplane_identity.py +0 -0
  71. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_agents.py +0 -0
  72. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_context.py +0 -0
  73. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_logs.py +0 -0
  74. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_quickstart.py +0 -0
  75. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_runtime.py +0 -0
  76. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_semdiff_provision.py +0 -0
  77. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_setup.py +0 -0
  78. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_sync.py +0 -0
  79. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_wizard_sync.py +0 -0
  80. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_workspaces.py +0 -0
  81. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/desktop_worktree.py +0 -0
  82. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/graph_availability.py +0 -0
  83. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/posthog_analytics.py +0 -0
  84. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/semantic_runtime.py +0 -0
  85. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/api/serve.py +0 -0
  86. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/context_projects/__init__.py +0 -0
  87. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/context_projects/airflow_discovery.py +0 -0
  88. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/context_projects/dbt_plugin.py +0 -0
  89. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/__init__.py +0 -0
  90. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/backward_dryrun.py +0 -0
  91. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/clustering_metrics.py +0 -0
  92. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/drop_snowflake_tables.py +0 -0
  93. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/ephemeral_lineage_report.py +0 -0
  94. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/eval_clustering.py +0 -0
  95. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/list_ephemeral_models.py +0 -0
  96. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/refingerprint_cache.py +0 -0
  97. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/regen_semantic_goldens.py +0 -0
  98. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/reset_git_workspace.py +0 -0
  99. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/reset_linear_workspace.py +0 -0
  100. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/reset_project.py +0 -0
  101. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/run_dbt_workflow.py +0 -0
  102. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/seed_census.py +0 -0
  103. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/semantic_goldens.py +0 -0
  104. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/__init__.py +0 -0
  105. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_bench.py +0 -0
  106. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_cells.py +0 -0
  107. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_corpus.py +0 -0
  108. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_corpus_dump.py +0 -0
  109. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_coverage.py +0 -0
  110. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_coverage_report.py +0 -0
  111. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_oracle.py +0 -0
  112. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_quality.py +0 -0
  113. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/fts_quality_bench.py +0 -0
  114. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/log_parse.py +0 -0
  115. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/report.py +0 -0
  116. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/surreal_bench/runner.py +0 -0
  117. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/__init__.py +0 -0
  118. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/README.md +0 -0
  119. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/__init__.py +0 -0
  120. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/cli.py +0 -0
  121. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/db_names.py +0 -0
  122. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/dbt_compile.py +0 -0
  123. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/diff_runner.py +0 -0
  124. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/expected_changes.py +0 -0
  125. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/fixture.yaml +0 -0
  126. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/column_rename_downstream/mutation.diff +0 -0
  127. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/fixture.yaml +0 -0
  128. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_coarsen/mutation.diff +0 -0
  129. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/fixture.yaml +0 -0
  130. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_deleted_leaf_model/mutation.diff +0 -0
  131. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/fixture.yaml +0 -0
  132. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_fanout_compound_oli/mutation.diff +0 -0
  133. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/fixture.yaml +0 -0
  134. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_gold_control/mutation.diff +0 -0
  135. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/fixture.yaml +0 -0
  136. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures/grain_v1_003_user_telemetry/mutation.diff +0 -0
  137. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/fixtures_loader.py +0 -0
  138. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/ingest.py +0 -0
  139. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/lifecycle.py +0 -0
  140. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/semantic_diff/source_prep.py +0 -0
  141. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/surreal/__init__.py +0 -0
  142. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/surreal/desktop_mcp.py +0 -0
  143. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/devtools/unified_graph/unified_cli.py +0 -0
  144. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/mcp/__init__.py +0 -0
  145. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/mcp/runner.py +0 -0
  146. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/mcp/server.py +0 -0
  147. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/templates/__init__.py +0 -0
  148. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/templates/typedef_config.yaml.j2 +0 -0
  149. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/typedef_cli.py +0 -0
  150. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/__init__.py +0 -0
  151. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/databricks.py +0 -0
  152. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/databricks_oauth.py +0 -0
  153. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/dbt.py +0 -0
  154. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/dbt_profile.py +0 -0
  155. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/env.py +0 -0
  156. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/gitignore.py +0 -0
  157. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/logging.py +0 -0
  158. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/src/lineage/utils/snowflake.py +0 -0
  159. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/__init__.py +0 -0
  160. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_app_lifespan.py +0 -0
  161. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_concept_cards.py +0 -0
  162. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_databricks_auth_wiring.py +0 -0
  163. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_agents_clone_backend.py +0 -0
  164. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_agents_runtime.py +0 -0
  165. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_agents_ticket_resolution.py +0 -0
  166. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_airflow_snapshot.py +0 -0
  167. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_clerk_auth.py +0 -0
  168. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_context_reader_cache.py +0 -0
  169. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databases_delete.py +0 -0
  170. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databases_edit.py +0 -0
  171. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databricks_oauth.py +0 -0
  172. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_databricks_setup.py +0 -0
  173. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_delete_endpoints.py +0 -0
  174. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_logs.py +0 -0
  175. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_orphan_flags.py +0 -0
  176. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_posthog.py +0 -0
  177. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_quickstart.py +0 -0
  178. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_repo_projects_discovery.py +0 -0
  179. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_runtime.py +0 -0
  180. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_salesforce.py +0 -0
  181. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_save_env_credentials.py +0 -0
  182. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_search.py +0 -0
  183. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_secondary_roles_hook.py +0 -0
  184. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_semdiff_provision.py +0 -0
  185. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_setup.py +0 -0
  186. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_setup_clone.py +0 -0
  187. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_setup_env_vars.py +0 -0
  188. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_sync.py +0 -0
  189. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_sync_render_profile.py +0 -0
  190. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_telemetry.py +0 -0
  191. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces.py +0 -0
  192. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_adopt.py +0 -0
  193. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_context_kinds.py +0 -0
  194. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_delete.py +0 -0
  195. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_gold_triggers.py +0 -0
  196. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_integrations.py +0 -0
  197. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_preview_name.py +0 -0
  198. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_projects_and_repos.py +0 -0
  199. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_r3_r10.py +0 -0
  200. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_summary.py +0 -0
  201. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_target_scope.py +0 -0
  202. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_workspaces_wizard.py +0 -0
  203. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree.py +0 -0
  204. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_clone.py +0 -0
  205. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_clone_token_scope.py +0 -0
  206. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_gold_queue.py +0 -0
  207. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_kind_target.py +0 -0
  208. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_desktop_worktree_resolution_scope.py +0 -0
  209. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_graph_availability_any_sentinel.py +0 -0
  210. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_no_legacy_surface.py +0 -0
  211. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_surreal_graph_availability.py +0 -0
  212. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_target_envelope_reads.py +0 -0
  213. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_validate_project_collisions.py +0 -0
  214. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_wizard_create_project_integration_persistence.py +0 -0
  215. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_workspace_clone_settings.py +0 -0
  216. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/api/test_worktree_registry_workspaces_shape.py +0 -0
  217. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/__init__.py +0 -0
  218. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/lineage/__init__.py +0 -0
  219. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/lineage/test_search_enum_coverage.py +0 -0
  220. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/backends/lineage/test_surreal_search.py +0 -0
  221. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/conftest.py +0 -0
  222. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/__init__.py +0 -0
  223. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/__init__.py +0 -0
  224. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_fts_cells.py +0 -0
  225. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_fts_coverage.py +0 -0
  226. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_fts_quality.py +0 -0
  227. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_log_parse.py +0 -0
  228. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/surreal_bench/test_report.py +0 -0
  229. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/test_typedef_home_provision.py +0 -0
  230. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/devtools/test_worktree_reclaim.py +0 -0
  231. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/docs/test_desktop_posthog_event_docs.py +0 -0
  232. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/integration/__init__.py +0 -0
  233. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/integration/test_semantic_diff_harness.py +0 -0
  234. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/__init__.py +0 -0
  235. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/test_desktop_mcp_retry.py +0 -0
  236. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/test_list_projects.py +0 -0
  237. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/mcp/test_server.py +0 -0
  238. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_gitignore_utils.py +0 -0
  239. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_helpers_semantic.py +0 -0
  240. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_model_tier_validation.py +0 -0
  241. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_template_yaml_escaping.py +0 -0
  242. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_typedef_cli_import_budget.py +0 -0
  243. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_unified_cli_airflow.py +0 -0
  244. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_unified_cli_fast.py +0 -0
  245. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/test_unified_cli_semantic.py +0 -0
  246. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/context_projects/test_airflow_discovery.py +0 -0
  247. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/context_projects/test_dbt_plugin.py +0 -0
  248. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/__init__.py +0 -0
  249. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_expected_changes.py +0 -0
  250. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_harness_units.py +0 -0
  251. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_lifecycle.py +0 -0
  252. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_lifecycle_io.py +0 -0
  253. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/devtools/test_semdiff_lifecycle_provisioning.py +0 -0
  254. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/formula/__init__.py +0 -0
  255. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/__init__.py +0 -0
  256. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_databricks_utils.py +0 -0
  257. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_dbt_docs_generate.py +0 -0
  258. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_dbt_profile.py +0 -0
  259. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_logging.py +0 -0
  260. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_run_dbt_command.py +0 -0
  261. {typedef_data_intelligence-0.35.0 → typedef_data_intelligence-0.36.0}/tests/unit/utils/test_snowflake_utils.py +0 -0
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [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)
4
+
5
+
6
+ ### Features
7
+
8
+ * **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))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **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))
14
+
3
15
  ## [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
16
 
5
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: typedef-data-intelligence
3
- Version: 0.35.0
3
+ Version: 0.36.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.36.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"
@@ -8,7 +8,6 @@ from dataclasses import dataclass
8
8
  from pathlib import Path
9
9
  from typing import 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
 
@@ -396,9 +399,9 @@ async def lifespan(app: FastAPI):
396
399
  # If user opts out via the consent modal or Settings, takes effect on next launch.
397
400
  from lineage.api.desktop_setup import _read_telemetry_prefs
398
401
 
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)
402
+ configure_desktop_logfire(
403
+ telemetry_opted_out=bool(_read_telemetry_prefs().get("telemetry_opted_out"))
404
+ )
402
405
 
403
406
  # Startup: no maintained API route initializes the legacy state path.
404
407
  if _NEEDS_LEGACY_STATE:
@@ -759,6 +762,7 @@ async def lifespan(app: FastAPI):
759
762
  shutdown_shared_posthog_client()
760
763
  except Exception:
761
764
  logger.debug("Failed to shut down PostHog analytics client", exc_info=True)
765
+ shutdown_desktop_logfire()
762
766
 
763
767
  # Close standalone data backend (e.g. DuckDB file handle)
764
768
  standalone_db = getattr(app.state, "standalone_data_backend", None)
@@ -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,62 @@
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
+
22
+
23
+ class NdjsonFileSpanExporter(SpanExporter):
24
+ """Write OpenTelemetry spans to a per-process NDJSON file."""
25
+
26
+ def __init__(self, traces_dir: Path) -> None:
27
+ """Create an exporter rooted at the configured traces directory."""
28
+ self._traces_dir = traces_dir
29
+ self._path = (
30
+ traces_dir / f"{_TRACE_FILE_PREFIX}-{os.getpid()}-{time.time_ns()}.ndjson"
31
+ )
32
+ self._lock = threading.Lock()
33
+ self._closed = False
34
+
35
+ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
36
+ """Append spans as one JSON object per line."""
37
+ with self._lock:
38
+ if self._closed:
39
+ return SpanExportResult.FAILURE
40
+ try:
41
+ self._traces_dir.mkdir(parents=True, exist_ok=True)
42
+ with self._path.open("a", encoding="utf-8") as trace_file:
43
+ for span in spans:
44
+ trace_file.write(span.to_json(indent=None))
45
+ trace_file.write("\n")
46
+ except (OSError, TypeError, ValueError):
47
+ return SpanExportResult.FAILURE
48
+ return SpanExportResult.SUCCESS
49
+
50
+ def shutdown(self) -> None:
51
+ """Stop accepting new spans."""
52
+ with self._lock:
53
+ self._closed = True
54
+
55
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
56
+ """Report that file writes are already flushed."""
57
+ return True
58
+
59
+
60
+ def make_local_span_processor() -> BatchSpanProcessor:
61
+ """Create the always-on local span processor for desktop startup."""
62
+ return BatchSpanProcessor(NdjsonFileSpanExporter(logs_dir() / "traces"))
@@ -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(
@@ -0,0 +1,257 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ from collections.abc import Iterator, Sequence
6
+ from pathlib import Path
7
+ from typing import NotRequired, TypedDict, Unpack
8
+
9
+ import core.paths as core_paths
10
+ import pytest
11
+ from opentelemetry.context import Context
12
+ from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, TracerProvider
13
+ from opentelemetry.sdk.trace.export import SpanExportResult
14
+ from opentelemetry.trace import Span
15
+
16
+ _OTEL_TEST_ENV_KEYS = (
17
+ "TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT",
18
+ "OTEL_EXPORTER_OTLP_PROTOCOL",
19
+ "OTEL_EXPORTER_OTLP_TRACES_PROTOCOL",
20
+ "OTEL_EXPORTER_OTLP_ENDPOINT",
21
+ "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT",
22
+ "OTEL_TRACES_EXPORTER",
23
+ "OTEL_METRICS_EXPORTER",
24
+ "OTEL_LOGS_EXPORTER",
25
+ )
26
+
27
+
28
+ class LogfireConfigureCall(TypedDict):
29
+ additional_span_processors: Sequence[SpanProcessor]
30
+ console: bool
31
+ send_to_logfire: bool
32
+ token: NotRequired[str | None]
33
+
34
+
35
+ @pytest.fixture(autouse=True)
36
+ def restore_desktop_logfire_env(
37
+ monkeypatch: pytest.MonkeyPatch,
38
+ ) -> Iterator[None]:
39
+ from lineage.observability import desktop_logfire
40
+
41
+ desktop_logfire._restore_local_only_otel_exporters()
42
+ for key in _OTEL_TEST_ENV_KEYS:
43
+ monkeypatch.delenv(key, raising=False)
44
+ yield
45
+ desktop_logfire._restore_local_only_otel_exporters()
46
+
47
+
48
+ def _set_typedef_home(monkeypatch, tmp_path: Path) -> Path:
49
+ home = tmp_path / "typedef-home"
50
+ monkeypatch.setenv("TYPEDEF_DEV_MODE", "1")
51
+ monkeypatch.setenv("TYPEDEF_HOME", str(home))
52
+ core_paths.typedef_home.cache_clear()
53
+ return home
54
+
55
+
56
+ def _capture_logfire_configure(monkeypatch) -> list[LogfireConfigureCall]:
57
+ from lineage.observability import desktop_logfire
58
+
59
+ calls: list[LogfireConfigureCall] = []
60
+
61
+ def fake_configure(**kwargs: Unpack[LogfireConfigureCall]) -> None:
62
+ calls.append(kwargs)
63
+
64
+ monkeypatch.setattr(desktop_logfire.logfire, "configure", fake_configure)
65
+ return calls
66
+
67
+
68
+ class RecordingSpanProcessor(SpanProcessor):
69
+ def on_start(self, span: Span, parent_context: Context | None = None) -> None:
70
+ return
71
+
72
+ def on_end(self, span: ReadableSpan) -> None:
73
+ return
74
+
75
+ def shutdown(self) -> None:
76
+ return
77
+
78
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
79
+ return True
80
+
81
+
82
+ class CapturingSpanProcessor(SpanProcessor):
83
+ def __init__(self) -> None:
84
+ self.spans: list[ReadableSpan] = []
85
+
86
+ def on_start(self, span: Span, parent_context: Context | None = None) -> None:
87
+ return
88
+
89
+ def on_end(self, span: ReadableSpan) -> None:
90
+ self.spans.append(span)
91
+
92
+ def shutdown(self) -> None:
93
+ return
94
+
95
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
96
+ return True
97
+
98
+
99
+ def test_ndjson_file_span_exporter_writes_sdk_span(tmp_path: Path) -> None:
100
+ from lineage.observability.local_span_export import NdjsonFileSpanExporter
101
+
102
+ processor = CapturingSpanProcessor()
103
+ provider = TracerProvider()
104
+ provider.add_span_processor(processor)
105
+ tracer = provider.get_tracer(__name__)
106
+
107
+ with tracer.start_as_current_span("typedef-test-span"):
108
+ pass
109
+
110
+ traces_dir = tmp_path / "traces"
111
+ exporter = NdjsonFileSpanExporter(traces_dir)
112
+
113
+ assert exporter.export(processor.spans) is SpanExportResult.SUCCESS
114
+ trace_files = list(traces_dir.glob("spans-*.ndjson"))
115
+ assert len(trace_files) == 1
116
+ lines = trace_files[0].read_text(encoding="utf-8").splitlines()
117
+ assert len(lines) == 1
118
+ assert json.loads(lines[0])["name"] == "typedef-test-span"
119
+
120
+
121
+ def test_configures_local_span_export_when_logfire_token_is_absent(
122
+ monkeypatch,
123
+ tmp_path: Path,
124
+ ) -> None:
125
+ from lineage.observability import desktop_logfire
126
+
127
+ _set_typedef_home(monkeypatch, tmp_path)
128
+ monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
129
+ calls = _capture_logfire_configure(monkeypatch)
130
+
131
+ desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
132
+
133
+ assert len(calls) == 1
134
+ assert calls[0]["send_to_logfire"] is False
135
+ assert calls[0]["token"] is None
136
+ assert calls[0]["console"] is False
137
+ assert len(calls[0]["additional_span_processors"]) == 1
138
+
139
+
140
+ def test_local_only_configuration_suppresses_env_otlp_exporters(
141
+ monkeypatch,
142
+ tmp_path: Path,
143
+ ) -> None:
144
+ from lineage.observability import desktop_logfire
145
+
146
+ _set_typedef_home(monkeypatch, tmp_path)
147
+ monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
148
+ monkeypatch.setenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "https://otel.example")
149
+ monkeypatch.setenv("OTEL_TRACES_EXPORTER", "otlp")
150
+ monkeypatch.setenv("OTEL_METRICS_EXPORTER", "otlp")
151
+ monkeypatch.setenv("OTEL_LOGS_EXPORTER", "otlp")
152
+ observed_exporters: list[tuple[str | None, str | None, str | None]] = []
153
+
154
+ def fake_configure(**kwargs: Unpack[LogfireConfigureCall]) -> None:
155
+ observed_exporters.append(
156
+ (
157
+ os.getenv("OTEL_TRACES_EXPORTER"),
158
+ os.getenv("OTEL_METRICS_EXPORTER"),
159
+ os.getenv("OTEL_LOGS_EXPORTER"),
160
+ )
161
+ )
162
+
163
+ monkeypatch.setattr(desktop_logfire.logfire, "configure", fake_configure)
164
+ monkeypatch.setattr(desktop_logfire.logfire, "shutdown", lambda: None)
165
+
166
+ desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
167
+
168
+ assert observed_exporters == [("none", "none", "none")]
169
+ assert os.getenv("OTEL_TRACES_EXPORTER") == "none"
170
+ assert os.getenv("OTEL_METRICS_EXPORTER") == "none"
171
+ assert os.getenv("OTEL_LOGS_EXPORTER") == "none"
172
+
173
+ desktop_logfire.shutdown_desktop_logfire()
174
+
175
+ assert os.getenv("OTEL_TRACES_EXPORTER") == "otlp"
176
+ assert os.getenv("OTEL_METRICS_EXPORTER") == "otlp"
177
+ assert os.getenv("OTEL_LOGS_EXPORTER") == "otlp"
178
+
179
+
180
+ def test_local_only_configuration_adds_explicit_local_otlp_processor(
181
+ monkeypatch,
182
+ tmp_path: Path,
183
+ ) -> None:
184
+ from lineage.observability import desktop_logfire
185
+
186
+ _set_typedef_home(monkeypatch, tmp_path)
187
+ local_otlp_processor = RecordingSpanProcessor()
188
+ monkeypatch.delenv("LOGFIRE_TOKEN", raising=False)
189
+ monkeypatch.setenv("TYPEDEF_ENABLE_LOCAL_OTLP_EXPORT", "1")
190
+ monkeypatch.setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://127.0.0.1:4318")
191
+ monkeypatch.setenv("OTEL_TRACES_EXPORTER", "otlp")
192
+ monkeypatch.setattr(
193
+ desktop_logfire,
194
+ "make_local_otlp_span_processor",
195
+ lambda: local_otlp_processor,
196
+ )
197
+ calls = _capture_logfire_configure(monkeypatch)
198
+
199
+ desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
200
+
201
+ assert len(calls) == 1
202
+ assert calls[0]["send_to_logfire"] is False
203
+ assert calls[0]["additional_span_processors"][1] is local_otlp_processor
204
+ assert os.getenv("OTEL_TRACES_EXPORTER") == "none"
205
+
206
+
207
+ def test_configures_cloud_and_local_span_export_with_token_and_consent(
208
+ monkeypatch,
209
+ tmp_path: Path,
210
+ ) -> None:
211
+ from lineage.observability import desktop_logfire
212
+
213
+ _set_typedef_home(monkeypatch, tmp_path)
214
+ monkeypatch.setenv("LOGFIRE_TOKEN", "test-token")
215
+ calls = _capture_logfire_configure(monkeypatch)
216
+
217
+ desktop_logfire.configure_desktop_logfire(telemetry_opted_out=False)
218
+
219
+ assert len(calls) == 1
220
+ assert calls[0]["send_to_logfire"] is True
221
+ assert calls[0]["token"] == "test-token"
222
+ assert calls[0]["console"] is False
223
+ assert len(calls[0]["additional_span_processors"]) == 1
224
+
225
+
226
+ def test_configures_local_only_when_token_exists_but_user_opted_out(
227
+ monkeypatch,
228
+ tmp_path: Path,
229
+ ) -> None:
230
+ from lineage.observability import desktop_logfire
231
+
232
+ _set_typedef_home(monkeypatch, tmp_path)
233
+ monkeypatch.setenv("LOGFIRE_TOKEN", "test-token")
234
+ calls = _capture_logfire_configure(monkeypatch)
235
+
236
+ desktop_logfire.configure_desktop_logfire(telemetry_opted_out=True)
237
+
238
+ assert len(calls) == 1
239
+ assert calls[0]["send_to_logfire"] is False
240
+ assert calls[0]["token"] is None
241
+ assert calls[0]["console"] is False
242
+ assert len(calls[0]["additional_span_processors"]) == 1
243
+
244
+
245
+ def test_shutdown_flushes_desktop_logfire(monkeypatch) -> None:
246
+ from lineage.observability import desktop_logfire
247
+
248
+ shutdown_calls: list[bool] = []
249
+
250
+ def fake_shutdown() -> None:
251
+ shutdown_calls.append(True)
252
+
253
+ monkeypatch.setattr(desktop_logfire.logfire, "shutdown", fake_shutdown)
254
+
255
+ desktop_logfire.shutdown_desktop_logfire()
256
+
257
+ assert shutdown_calls == [True]