sql-code-graph 1.1.3__tar.gz → 1.2.2__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 (429) hide show
  1. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/ARCHITECTURE_REVIEW.md +198 -1
  2. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/CLAUDE.md +21 -0
  3. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/PKG-INFO +11 -1
  4. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/README.md +10 -0
  5. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/docs/cli.md +98 -8
  6. sql_code_graph-1.2.2/plan/fix_downstream_sink_location.md +119 -0
  7. sql_code_graph-1.2.2/plan/v1_2_0_read_proxy.md +597 -0
  8. sql_code_graph-1.2.2/plan/v1_2_1_bugfix.md +669 -0
  9. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/pyproject.toml +1 -1
  10. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/__init__.py +1 -1
  11. sql_code_graph-1.2.2/src/sqlcg/cli/commands/analyze.py +327 -0
  12. sql_code_graph-1.2.2/src/sqlcg/cli/commands/db.py +187 -0
  13. sql_code_graph-1.2.2/src/sqlcg/cli/commands/find.py +78 -0
  14. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/gain.py +13 -11
  15. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/config.py +15 -13
  16. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/indexer.py +91 -11
  17. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/lineage/aggregator.py +17 -45
  18. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/ansi_parser.py +2 -2
  19. sql_code_graph-1.2.2/src/sqlcg/server/read_client.py +192 -0
  20. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/server.py +97 -18
  21. sql_code_graph-1.2.2/tests/integration/test_cross_file_lineage.py +125 -0
  22. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_indexer_commits.py +7 -2
  23. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_pr3_kind_tagging.py +24 -19
  24. sql_code_graph-1.2.2/tests/integration/test_read_via_server.py +229 -0
  25. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_readonly_under_lock.py +31 -22
  26. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_star_resolution.py +10 -2
  27. sql_code_graph-1.2.2/tests/integration/test_user_surface_recall_guard.py +455 -0
  28. sql_code_graph-1.2.2/tests/unit/test_aggregator.py +68 -0
  29. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_aggregator_skip.py +21 -30
  30. sql_code_graph-1.2.2/tests/unit/test_canonical_target_resolution.py +270 -0
  31. sql_code_graph-1.2.2/tests/unit/test_db_info.py +142 -0
  32. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_find_cmd.py +19 -28
  33. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_freshness_helper.py +20 -21
  34. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_gain_ratio.py +5 -12
  35. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_hygiene_config_warning.py +2 -4
  36. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_noise_filter.py +8 -2
  37. sql_code_graph-1.2.2/tests/unit/test_read_client.py +234 -0
  38. sql_code_graph-1.2.2/tests/unit/test_resolve_pass2_passes_dependency_filter.py +174 -0
  39. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_star_schema_unit.py +26 -25
  40. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_unqualified_fallback.py +9 -24
  41. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/uv.lock +1 -1
  42. sql_code_graph-1.1.3/src/sqlcg/cli/commands/analyze.py +0 -325
  43. sql_code_graph-1.1.3/src/sqlcg/cli/commands/db.py +0 -181
  44. sql_code_graph-1.1.3/src/sqlcg/cli/commands/find.py +0 -81
  45. sql_code_graph-1.1.3/tests/integration/test_cross_file_lineage.py +0 -129
  46. sql_code_graph-1.1.3/tests/unit/test_aggregator.py +0 -55
  47. sql_code_graph-1.1.3/tests/unit/test_db_info.py +0 -173
  48. sql_code_graph-1.1.3/tests/unit/test_resolve_pass2_passes_dependency_filter.py +0 -149
  49. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/api-documenter.md +0 -0
  50. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/architect-planner.md +0 -0
  51. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/architect-reviewer.md +0 -0
  52. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/code-reviewer.md +0 -0
  53. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/developer.md +0 -0
  54. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/plan-reviewer.md +0 -0
  55. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.claude/agents/sprint-planner.md +0 -0
  56. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  57. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  58. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  59. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/workflows/benchmark.yml +0 -0
  60. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/workflows/e2e-tests.yml +0 -0
  61. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/workflows/release.yml +0 -0
  62. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.github/workflows/test.yml +0 -0
  63. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.gitignore +0 -0
  64. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.pre-commit-config.yaml +0 -0
  65. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/.sqlcgignore +0 -0
  66. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/CHANGELOG.md +0 -0
  67. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/docs/AIRBNB_PARSE_REPORT.md +0 -0
  68. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/e2e_firstuser_report.md +0 -0
  69. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/e2e_run_20260528_101413.output +0 -0
  70. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/main.py +0 -0
  71. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/WORKFLOW.md +0 -0
  72. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/blueprint.md +0 -0
  73. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/bundle_claude_skill.md +0 -0
  74. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/feature_34_unused_presentation_segregation.md +0 -0
  75. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/feature_35_external_downstream_injection.md +0 -0
  76. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/feature_F2_bundle_claude_skill.md +0 -0
  77. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/fix_dynamic_table_parsing.md +0 -0
  78. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/fix_expand_qualify_perf.md +0 -0
  79. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/fix_firstuser_findings.md +0 -0
  80. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/fix_schema_case_mismatch.md +0 -0
  81. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/hygiene_config_path_and_survivors.md +0 -0
  82. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/investigation_e5_e4.md +0 -0
  83. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/living_codebase_resync.md +0 -0
  84. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/measurements/schema_comparison_with_schema.json +0 -0
  85. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/measurements/schema_comparison_without_schema.json +0 -0
  86. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/measurements/sprint_08_changelogs_fullindex.json +0 -0
  87. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/measurements/sprint_08_fullcorpus_index.json +0 -0
  88. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/measurements/sprint_pool_300s_plan.json +0 -0
  89. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/parse_diagnostics.md +0 -0
  90. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/parsing_errors_experiment.md +0 -0
  91. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/snowflake_en_test_suite.md +0 -0
  92. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_01.md +0 -0
  93. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_01_deployment_pypi.md +0 -0
  94. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_02.md +0 -0
  95. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_02_v0.3.0_core.md +0 -0
  96. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_03.md +0 -0
  97. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_03_v0.3.1_postmortem.md +0 -0
  98. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_04_column_lineage.md +0 -0
  99. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_04_column_lineage_fix.md +0 -0
  100. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_05_star_resolution.md +0 -0
  101. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_06_lineage_coverage.md +0 -0
  102. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_07_open_ecodes.md +0 -0
  103. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_07_perf_and_live_test.md +0 -0
  104. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_08_perf_upsert.md +0 -0
  105. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_09_lineage_coverage.md +0 -0
  106. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_10_anchor_tools.md +0 -0
  107. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_11_v1.0.2_bugfix.md +0 -0
  108. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_12_v1.1.0.md +0 -0
  109. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_13_v1.1.0_cluster_b.md +0 -0
  110. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sprint_3.1_postmortem.md +0 -0
  111. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/sqlcg.md +0 -0
  112. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/trust_layer.md +0 -0
  113. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/v1.1.0_cluster_b_provenance_trust.md +0 -0
  114. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/v1.1.0_live_graph_freshness.md +0 -0
  115. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/v1.1.1_batch_upsert_perf.md +0 -0
  116. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/v1_1_2_bugfix.md +0 -0
  117. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/plan/v1_1_3_union_cte_star.md +0 -0
  118. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/profile.html +0 -0
  119. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/pyrightconfig.json +0 -0
  120. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/scripts/collect_parse_errors.py +0 -0
  121. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/scripts/generate_cli_docs.sh +0 -0
  122. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/__main__.py +0 -0
  123. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/__init__.py +0 -0
  124. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/__init__.py +0 -0
  125. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/git.py +0 -0
  126. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/index.py +0 -0
  127. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/install.py +0 -0
  128. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/mcp.py +0 -0
  129. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/reindex.py +0 -0
  130. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/report.py +0 -0
  131. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/uninstall.py +0 -0
  132. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/commands/watch.py +0 -0
  133. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/cli/main.py +0 -0
  134. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/__init__.py +0 -0
  135. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/freshness.py +0 -0
  136. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/graph_db.py +0 -0
  137. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/jobs.py +0 -0
  138. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/kuzu_backend.py +0 -0
  139. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/neo4j_backend.py +0 -0
  140. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/queries.cypher +0 -0
  141. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/queries.py +0 -0
  142. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/schema.cypher +0 -0
  143. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/core/schema.py +0 -0
  144. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/__init__.py +0 -0
  145. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/dbt_adapter.py +0 -0
  146. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/error_classify.py +0 -0
  147. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/git_delta.py +0 -0
  148. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/pool.py +0 -0
  149. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/walker.py +0 -0
  150. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/indexer/watcher.py +0 -0
  151. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/lineage/__init__.py +0 -0
  152. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/lineage/schema_resolver.py +0 -0
  153. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/metrics/__init__.py +0 -0
  154. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/metrics/store.py +0 -0
  155. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/__init__.py +0 -0
  156. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/base.py +0 -0
  157. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/bigquery_parser.py +0 -0
  158. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/postgres_parser.py +0 -0
  159. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/registry.py +0 -0
  160. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/snowflake_parser.py +0 -0
  161. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/parsers/tsql_parser.py +0 -0
  162. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/__init__.py +0 -0
  163. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/control.py +0 -0
  164. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/exceptions.py +0 -0
  165. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/models.py +0 -0
  166. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/noise_filter.py +0 -0
  167. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/skill.py +0 -0
  168. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/server/tools.py +0 -0
  169. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/utils/__init__.py +0 -0
  170. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/utils/hashing.py +0 -0
  171. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/utils/ignore.py +0 -0
  172. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/src/sqlcg/utils/logging.py +0 -0
  173. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/__init__.py +0 -0
  174. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/__init__.py +0 -0
  175. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/adversarial/200_join.sql +0 -0
  176. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/adversarial/500_union.sql +0 -0
  177. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/bench_indexer.py +0 -0
  178. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/conftest.py +0 -0
  179. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/case_normalization.sql +0 -0
  180. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/colon_cast.sql +0 -0
  181. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/colon_reserved_word.sql +0 -0
  182. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/copy_into.sql +0 -0
  183. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/create_procedure.sql +0 -0
  184. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/identifier_dynamic.sql +0 -0
  185. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/lateral_flatten.sql +0 -0
  186. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/qualify.sql +0 -0
  187. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/scripting_block.sql +0 -0
  188. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/golden_corpus/snowflake/three_part.sql +0 -0
  189. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/tpch/q01.sql +0 -0
  190. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/tpch/q02.sql +0 -0
  191. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/tpch/q03.sql +0 -0
  192. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/tpch/q04.sql +0 -0
  193. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/benchmarks/tpch/q05.sql +0 -0
  194. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/__init__.py +0 -0
  195. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/conftest.py +0 -0
  196. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_F2_skill_install_e2e.py +0 -0
  197. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_airbnb_e2e.py +0 -0
  198. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_cli_index.py +0 -0
  199. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_git_hook_install.py +0 -0
  200. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_golden_lineage.py +0 -0
  201. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_mcp_lifecycle.py +0 -0
  202. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_mcp_tools.py +0 -0
  203. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_parse_diagnostics_cli.py +0 -0
  204. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_star_resolution_e2e.py +0 -0
  205. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/e2e/test_watch.py +0 -0
  206. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/dim_hosts_cleansed.sql +0 -0
  207. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/dim_listings_cleansed.sql +0 -0
  208. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/fct_reviews.sql +0 -0
  209. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/mart_fullmoon_reviews.sql +0 -0
  210. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/raw_hosts.sql +0 -0
  211. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/raw_listings.sql +0 -0
  212. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/raw_reviews.sql +0 -0
  213. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/src_hosts.sql +0 -0
  214. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/src_listings.sql +0 -0
  215. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/airbnb/src_reviews.sql +0 -0
  216. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/bigquery/.gitkeep +0 -0
  217. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/jaffle_shop/customers.sql +0 -0
  218. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/jaffle_shop/orders.sql +0 -0
  219. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/jaffle_shop/raw_orders.sql +0 -0
  220. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/snowflake/base_tables.sql +0 -0
  221. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/snowflake/reports.sql +0 -0
  222. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/snowflake/views.sql +0 -0
  223. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/star_corpus/ddl_src.sql +0 -0
  224. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/star_corpus/ddl_tgt.sql +0 -0
  225. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/star_corpus/etl_alias_star.sql +0 -0
  226. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/star_corpus/etl_star.sql +0 -0
  227. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/synthetic/base_tables.sql +0 -0
  228. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/synthetic/reports.sql +0 -0
  229. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/fixtures/synthetic/views.sql +0 -0
  230. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/__init__.py +0 -0
  231. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/snowflake/__init__.py +0 -0
  232. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/snowflake/test_insert_select.py +0 -0
  233. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_T34_presentation_segregation.py +0 -0
  234. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_T35_external_consumers.py +0 -0
  235. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_anchor_tools.py +0 -0
  236. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_bulk_upsert.py +0 -0
  237. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_column_lineage_e2e.py +0 -0
  238. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_cte_recall_guard.py +0 -0
  239. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_cte_schema_alias_guard.py +0 -0
  240. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_cte_source_node_invariant.py +0 -0
  241. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_dialect_matrix.py +0 -0
  242. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_e36_xfile_regression_guard.py +0 -0
  243. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_freshness_mcp.py +0 -0
  244. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_hygiene_config_root_reconciliation.py +0 -0
  245. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_indexer_batching.py +0 -0
  246. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_indexer_to_graph.py +0 -0
  247. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_live_anchors.py +0 -0
  248. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_parse_diagnostics.py +0 -0
  249. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_pr1_confidence_reason.py +0 -0
  250. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_pr2_source_location.py +0 -0
  251. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_reindex_via_server.py +0 -0
  252. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_resync.py +0 -0
  253. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_temp_table_lineage.py +0 -0
  254. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/integration/test_union_cte_star_recall_guard.py +0 -0
  255. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/perf/__init__.py +0 -0
  256. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/perf/test_perf.py +0 -0
  257. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E10/__init__.py +0 -0
  258. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E11/__init__.py +0 -0
  259. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E12/__init__.py +0 -0
  260. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E12/e12_json_path.sql +0 -0
  261. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E12/e12_lateral_flatten.sql +0 -0
  262. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E12/test_e12.py +0 -0
  263. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E13/__init__.py +0 -0
  264. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E14/__init__.py +0 -0
  265. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E15/__init__.py +0 -0
  266. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E16/__init__.py +0 -0
  267. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E16/e16_merge.sql +0 -0
  268. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E16/e16_merge_delete.sql +0 -0
  269. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E16/test_e16.py +0 -0
  270. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E17/__init__.py +0 -0
  271. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E18/__init__.py +0 -0
  272. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E18/e18_decode.sql +0 -0
  273. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E18/e18_iff_decode.sql +0 -0
  274. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E18/e18_nvl2.sql +0 -0
  275. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E18/test_e18.py +0 -0
  276. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E19/__init__.py +0 -0
  277. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E2/__init__.py +0 -0
  278. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E2/e2_expr_alias.sql +0 -0
  279. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E2/e2_function_alias.sql +0 -0
  280. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E2/e2_multiply_alias.sql +0 -0
  281. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E2/test_e2.py +0 -0
  282. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E20/__init__.py +0 -0
  283. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E21/__init__.py +0 -0
  284. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E21/e21_alias_forward_ref.sql +0 -0
  285. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E21/e21_three_level_chain.sql +0 -0
  286. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E21/test_e21.py +0 -0
  287. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E22/__init__.py +0 -0
  288. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E23/__init__.py +0 -0
  289. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E23/e23_stored_proc.sql +0 -0
  290. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E23/e23_stored_proc_multi.sql +0 -0
  291. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E23/test_e23.py +0 -0
  292. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E24/__init__.py +0 -0
  293. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E25/__init__.py +0 -0
  294. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E25/e25_cross_db.sql +0 -0
  295. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E25/e25_two_part.sql +0 -0
  296. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E25/test_e25.py +0 -0
  297. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E25/test_e25_full_id.py +0 -0
  298. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E26/__init__.py +0 -0
  299. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E27/__init__.py +0 -0
  300. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E27/e27_nested_udf.sql +0 -0
  301. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E27/e27_udf.sql +0 -0
  302. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E27/test_e27.py +0 -0
  303. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E28/__init__.py +0 -0
  304. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E29/__init__.py +0 -0
  305. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E3/__init__.py +0 -0
  306. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E3/e3_alter_table.sql +0 -0
  307. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E3/e3_create_sequence.sql +0 -0
  308. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E3/e3_ddl_only.sql +0 -0
  309. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E3/test_e3.py +0 -0
  310. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E30/__init__.py +0 -0
  311. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E31/__init__.py +0 -0
  312. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E32/__init__.py +0 -0
  313. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E33/__init__.py +0 -0
  314. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E34/__init__.py +0 -0
  315. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E35/__init__.py +0 -0
  316. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E36/__init__.py +0 -0
  317. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E36/e36_temp_multi_use.sql +0 -0
  318. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E36/e36_temp_table.sql +0 -0
  319. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E36/test_e36.py +0 -0
  320. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E36/test_e36_xfile.py +0 -0
  321. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E37/__init__.py +0 -0
  322. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E38/__init__.py +0 -0
  323. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E4/__init__.py +0 -0
  324. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E4/e4_execute_immediate.sql +0 -0
  325. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E4/e4_if_not_exists.sql +0 -0
  326. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E4/e4_unexpected_token.sql +0 -0
  327. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E4/e4_unpivot.sql +0 -0
  328. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E4/test_e4.py +0 -0
  329. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E5/__init__.py +0 -0
  330. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E5/e5_cte_missing_source.sql +0 -0
  331. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E5/e5_multi_cte.sql +0 -0
  332. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E5/e5_nested_cte.sql +0 -0
  333. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E5/test_e5.py +0 -0
  334. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E5/test_e5_cte_projection.py +0 -0
  335. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E8/__init__.py +0 -0
  336. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E8/e8_dynamic_sources.sql +0 -0
  337. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E8/e8_seq_nextval.sql +0 -0
  338. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E8/e8_uuid.sql +0 -0
  339. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E8/test_e8.py +0 -0
  340. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E9/__init__.py +0 -0
  341. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_aggregates/__init__.py +0 -0
  342. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_aggregates/fixture_sum_absent_cross_schema.sql +0 -0
  343. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_aggregates/fixture_sum_case_when.sql +0 -0
  344. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_aggregates/fixture_sum_present_source.sql +0 -0
  345. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_aggregates/test_e_aggregates.py +0 -0
  346. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_date_functions/__init__.py +0 -0
  347. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_date_functions/fixture_date_aliased.sql +0 -0
  348. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_date_functions/fixture_date_unaliased.sql +0 -0
  349. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_date_functions/fixture_datediff_unaliased.sql +0 -0
  350. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_date_functions/fixture_year_unaliased.sql +0 -0
  351. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/E_date_functions/test_e_date_functions.py +0 -0
  352. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/README.md +0 -0
  353. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/__init__.py +0 -0
  354. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/__init__.py +0 -0
  355. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/fixture_etl.sql +0 -0
  356. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/fixture_omloopsnelheid.sql +0 -0
  357. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/fixture_semantic.sql +0 -0
  358. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/fixture_source.sql +0 -0
  359. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/test_anchor_ma_aantal_op_order.py +0 -0
  360. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/anchors/test_anchor_omloopsnelheid.py +0 -0
  361. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/conftest.py +0 -0
  362. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/snowflake/test_plan_review_gates.py +0 -0
  363. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/__init__.py +0 -0
  364. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/snowflake/__init__.py +0 -0
  365. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/snowflake/test_scripting_noise.py +0 -0
  366. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_F2_install_skill.py +0 -0
  367. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_F2_skill_render.py +0 -0
  368. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_F2_uninstall_skill.py +0 -0
  369. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_T09_01_qualify_once.py +0 -0
  370. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_T09_02_ddl_skip.py +0 -0
  371. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_T09_04_subprocess_isolate.py +0 -0
  372. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_T09_06_log_verbosity.py +0 -0
  373. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_T35_config_external_consumers.py +0 -0
  374. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_base_parser.py +0 -0
  375. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_branch_monitor.py +0 -0
  376. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_bulk_upsert_invariant.py +0 -0
  377. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_cli.py +0 -0
  378. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_cli_help.py +0 -0
  379. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_closure_depth.py +0 -0
  380. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_column_lineage_wiring.py +0 -0
  381. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_config.py +0 -0
  382. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_data_models.py +0 -0
  383. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_dominant_cause.py +0 -0
  384. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_firstuser_findings.py +0 -0
  385. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_git_delta.py +0 -0
  386. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_git_hooks.py +0 -0
  387. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_git_hooks_notify.py +0 -0
  388. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_graph_backend.py +0 -0
  389. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_hard_kill_pool.py +0 -0
  390. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_include_working_tree.py +0 -0
  391. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_index_cmd.py +0 -0
  392. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_index_flags.py +0 -0
  393. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_index_progress.py +0 -0
  394. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_indexer_progress.py +0 -0
  395. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_indexer_quality.py +0 -0
  396. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_install.py +0 -0
  397. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_install_message.py +0 -0
  398. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_jobs.py +0 -0
  399. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_judgement.py +0 -0
  400. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_kuzu_backend.py +0 -0
  401. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_kuzu_lock.py +0 -0
  402. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_lineage_conversion.py +0 -0
  403. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_literal_column_skip.py +0 -0
  404. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_mcp_best_practices.py +0 -0
  405. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_mcp_control.py +0 -0
  406. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_mcp_stdio_smoke.py +0 -0
  407. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_metrics.py +0 -0
  408. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_parse_file_dependency_filter.py +0 -0
  409. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_parse_quality.py +0 -0
  410. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_parser.py +0 -0
  411. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_perf_scaling_guard.py +0 -0
  412. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_pr07_observability.py +0 -0
  413. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_queries_loader.py +0 -0
  414. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_schema_resolver.py +0 -0
  415. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_server.py +0 -0
  416. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_snowflake_strip_alter_set_tag.py +0 -0
  417. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_sprint_06_t04_t05.py +0 -0
  418. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_star_resolution_unit.py +0 -0
  419. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_submit_feedback.py +0 -0
  420. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_subprocess_isolate.py +0 -0
  421. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_t02_expression_name_extraction.py +0 -0
  422. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_t03_ddl_skip.py +0 -0
  423. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_timeout_cancel.py +0 -0
  424. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_tools_hints.py +0 -0
  425. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_tools_warnings.py +0 -0
  426. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_uninstall.py +0 -0
  427. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_upsert_batch_invariant.py +0 -0
  428. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_walker.py +0 -0
  429. {sql_code_graph-1.1.3 → sql_code_graph-1.2.2}/tests/unit/test_watcher.py +0 -0
@@ -478,7 +478,15 @@ both concrete backends override it — the blueprint does not enforce this.
478
478
  Risk: HIGH. A failed re-index leaves orphaned graph nodes that produce incorrect
479
479
  lineage results until the next full re-index.
480
480
 
481
- ### 3.2 [HIGH] `CrossFileAggregator.resolve_pass2` re-opens files without error handling
481
+ ### 3.2 ~~[HIGH]~~ **RESOLVED (v1.2.1)** `CrossFileAggregator.resolve_pass2` re-opens files without error handling
482
+
483
+ **Resolution (2026-06-02, v1.2.1, PR #48):** `resolve_pass2` was deleted entirely. It had
484
+ zero production call sites — the production pass-2 path in `index_repo` reimplements the
485
+ dep-filter + reparse logic inline (worker calls `parse_file(..., dependency_filter=...)`
486
+ in [`pool.py`](src/sqlcg/indexer/pool.py)), and that path already handles file-read errors
487
+ defensively. The dead method (and the four tests that exercised it) were the same
488
+ test-measures-a-dead-path blind-spot class as #40. Tests realigned to drive `index_repo`
489
+ end-to-end. See [`v1_2_1_bugfix.md`](plan/v1_2_1_bugfix.md) Phase 5.
482
490
 
483
491
  ```python
484
492
  def resolve_pass2(self, parser, parsed):
@@ -2947,4 +2955,193 @@ editor owns the stdio process lifecycle and v1.1 has no launcher to re-spawn it
2947
2955
  owner-only, but any same-uid local process can send `stop`/`reindex`).
2948
2956
  - **Freshness on every tool result by default** — v1.1 ships it gated/off to
2949
2957
  protect hot-path latency; default-on pending an overhead measurement.
2958
+
2959
+ ## 18. v1.2.1 — Lineage recall guards + canonical INSERT target + CTE filter fixes
2960
+
2961
+ Review date: 2026-06-02 · Reconciled by: architect-planner · Branch:
2962
+ `feat/v1.2.1-bugfix` · PR #48 · Verdict: **PLAN-COMPLIANT (PASS)**
2963
+
2964
+ > **Bundled release note (2026-06-02):** the v1.2.1 bugfix scope (this section) and the
2965
+ > downstream-sink `file:line` fix (§19.2) ship as a **single `v1.2.2` release** — the
2966
+ > lower `v1.2.1` tag is skipped (precedent: v1.1.2→v1.1.3). Both change sets live on
2967
+ > `feat/v1.2.1-bugfix` (PR #48, fast-forwarded to `b5bf7a3`); version is `1.2.2`. The
2968
+ > combined plan-compliance verdict (PASS, both scopes) is recorded in
2969
+ > [`v1_2_1_bugfix.md`](plan/v1_2_1_bugfix.md) → "Bundled v1.2.2 release compliance".
2970
+
2971
+ Patch release fixing the residual user-facing lineage defects (#44 canonical-name
2972
+ split, #45 `file:line=?` + `cte_*` leak), repairing the v1.2.0 read-proxy filter
2973
+ regression (the #39 read-half), deleting the dead `resolve_pass2` (resolves §3.2),
2974
+ and landing the #40 user-surface regression-guard suite. Plan:
2975
+ [`v1_2_1_bugfix.md`](plan/v1_2_1_bugfix.md) (full phase-by-phase compliance log appended there).
2976
+
2977
+ ### 18.1 #44 — canonical INSERT-target resolution
2978
+
2979
+ A new `CrossFileAggregator.canonical_by_bare` index (bare name → sole DDL `full_id`)
2980
+ plus `_ambiguous_bare` (names defined in >1 schema, never rewritten) is built in
2981
+ `register_pass1` from DDL tables only. It is threaded `index_repo` →
2982
+ `_upsert_file_batch` → `_build_file_rows`, where an unqualified / non-DDL INSERT
2983
+ target whose bare name resolves to a sole DDL canonical is rewritten to share that
2984
+ node's identity (`qualified` == DDL `full_id`, all fields derived from the canonical),
2985
+ keeping `kind:"table"`. The fix lands in the node-emission loop, **not** the deleted
2986
+ `resolve_pass2` and **not** the per-column lineage hot path — pure row-dict mutation,
2987
+ no new `execute()`. Degrades to a no-op when `canonical_by_bare is None`
2988
+ (single-file `reindex_file` / `resync_changed` paths) — known limitation, re-index is
2989
+ the migration path per CLAUDE.md.
2990
+
2991
+ ### 18.2 #45 — `file:line` recall + structural `cte_*` suppression
2992
+
2993
+ - **#45.1**: `analyze upstream/downstream` now bind location from the source's
2994
+ *outgoing* `COLUMN_LINEAGE` edge and aggregate `WITH … min(start_line), min(file_path)`
2995
+ **before** `LIMIT 100`, so multi-hop sources carry a real `file:line` and the limit
2996
+ counts distinct sources, not edge-fanout rows.
2997
+ - **#45.2**: chosen mechanism is **graph-truth, not a name heuristic** — unresolved
2998
+ synthetic INSERT targets are emitted `kind:"derived"` (the #44 emission change), and
2999
+ the restored `kind IN ['table','external']` read filter excludes them. No name-prefix
3000
+ belt was needed.
3001
+
3002
+ ### 18.3 Two implementation invariants discovered during the fix
3003
+
3004
+ Both are now load-bearing; recorded so a future refactor does not silently undo them.
3005
+
3006
+ 1. **KuzuDB: a `WHERE` clause inside `OPTIONAL MATCH` filters the match attempt, not
3007
+ the surrounding row.** To filter rows on an optionally-matched node's property, an
3008
+ explicit `WITH <carried>, t WHERE …` must follow. `analyze._kind_filter` uses this
3009
+ form (`OPTIONAL MATCH (t:SqlTable {…}) WITH <alias>, t WHERE t.kind IS NULL OR …`).
3010
+ The `IS NULL` branch is intentional: it keeps node-less physical sources (the #39
3011
+ tail) while CTE/derived intermediates (`kind='cte'`/`'derived'`) are excluded.
3012
+ Guarded by TC1/TC4 in [`test_user_surface_recall_guard.py`](tests/integration/test_user_surface_recall_guard.py).
3013
+ 2. **`_flush_row_batch` dedup must prefer structural kinds over the default `'table'`.**
3014
+ A CTE alias is emitted twice in a batch — first `kind='table'` (incidental source
3015
+ reference) then `kind='cte'` (CTE destination). The pre-existing first-seen-wins
3016
+ dedup kept the wrong one, defeating #45.2. New Rule 2: `{'cte','derived','external'}`
3017
+ beats default `'table'` (Rule 1 — DDL provenance via `defined_in_file` — still takes
3018
+ precedence). This logic lives in the documented `_flush_row_batch` perf hot path but
3019
+ is pure dict comparison in the existing dedup loop — no change to flush cardinality,
3020
+ batch/scaling guards stay green (CLAUDE.md performance invariants intact).
3021
+
3022
+ ### 18.4 Resolved / out-of-scope
3023
+
3024
+ - **§3.2 `resolve_pass2`** — RESOLVED (deleted; see §3.2 update).
3025
+ - **Residual #38 (UNION-ALL sibling CTEs)** — already fixed in 1.1.3; live DWH
3026
+ re-verification (80→47 `cte_insert` islands) recorded in the plan; the surviving 47
3027
+ are likely legitimately source-less computed measures, not a regression. Shape B of
3028
+ the #40 guard now asserts a computed-measure projection so a real gap there reds the guard.
3029
+ - **#28 server read-only-by-default + reindex escalation** — out of 1.2.1; recommended
3030
+ as a separate minor (1.3.0). The read half is already handled by the v1.2.0 routing proxy.
2950
3031
  - **`connected_clients` fidelity** — best-effort `1` today; no real counter.
3032
+
3033
+ ## 19. v1.2.1 live DWH re-verification — two residual findings (2026-06-02)
3034
+
3035
+ Source: live verification of the v1.2.1 fixes against the full DWH corpus
3036
+ (`../dwh`, 1,335 SQL files, clean index ~1:56). **The v1.2.1 fixes themselves
3037
+ PASSED** — both findings below are residual / design-limit behaviour surfaced by
3038
+ the run, not regressions in PR #48. Corpus graph snapshot (kind-typed `SqlTable`):
3039
+ 2,222 `table` / 383 `cte` / 272 `derived`.
3040
+
3041
+ ### 19.1 [KEEP — LOW] Residual scratch-object leak into the default `analyze` surface
3042
+
3043
+ **Measurement.** The #45.2 fix essentially closed the `cte_` leak it targeted:
3044
+ of nodes still typed `kind='table'`, only 3 carry a `cte_` prefix (vs 383 now
3045
+ correctly `kind='cte'`). The broader leak persists: **495 of 2,222 `table`-kind
3046
+ nodes (~22%) carry scratch-object name prefixes** — 471 `tmp_*`, 21 `temp_*`,
3047
+ 3 `cte_*` — plus ~575 unqualified single-token nodes (a mix of real bare tables
3048
+ and CTE aliases). These surface in default (filtered) `analyze` output because the
3049
+ filter is correctly kind-based (graph-truth) but these aliases were never classified
3050
+ `cte`/`derived` at parse/aggregation time. This is the known #38 islands residual
3051
+ (s18.4 / `47 cte_insert islands`); the leak is now `tmp_*`-dominated, not
3052
+ `cte_*`-dominated. See memory `project_issue38_cte_filter_regression`.
3053
+
3054
+ **Decision: KEEP as a future ticket, LOW priority.** Rationale tied to existing
3055
+ decisions:
3056
+ - The standing **"no name-prefix filtering"** decision (s18.2; CLAUDE.md) is correct
3057
+ and stays. The fix therefore **cannot** live in the filter — it must happen at
3058
+ **classification time** in the aggregator/parser, exactly like the #44 synthetic
3059
+ INSERT-target → `kind='derived'` emission change (s18.1). A `CREATE [OR REPLACE]
3060
+ TEMP/TRANSIENT TABLE tmp_x AS SELECT …` whose target is a scratch object should be
3061
+ emitted `kind='derived'` at `_build_file_rows` time, the same place #44 already
3062
+ rewrites node identity. 492/495 of the leak (`tmp_*`/`temp_*`) are this CTAS-into-
3063
+ scratch shape — addressable by classifying on the parsed CTAS-into-temp statement
3064
+ kind (DDL-derived structural truth), **not** on the name prefix.
3065
+ - **Non-goal for the ticket:** the ~575 unqualified single-token nodes. They are a
3066
+ genuine mix of real bare tables and CTE aliases; auto-classifying them risks
3067
+ hiding real physical tables from the default surface (a false-negative worse than
3068
+ the current cosmetic leak). They stay `kind='table'` until a structural signal
3069
+ (CTE membership, CTAS target) is available — never a name guess.
3070
+
3071
+ **Risk/reward.** Reward: cleaner default `analyze` surface, ~22% fewer scratch
3072
+ intermediates leaking; closes the visible tail of #38. Risk: medium — must classify
3073
+ on parsed statement structure (TEMP/TRANSIENT CTAS target), and must not reclassify
3074
+ a real `tmp_`-named physical table that is read by a non-CTAS statement; needs a guard
3075
+ asserting a real table named `tmp_*` is still surfaced. Effort: medium (one emission
3076
+ site, reuses #44 plumbing, plus a behavioural guard). Below the open HIGH §3.x items
3077
+ and Finding 19.2 in the ranking. Recorded here; no plan stub drafted yet (pull when
3078
+ a lineage-coverage sprint next touches `_build_file_rows`).
3079
+
3080
+ ### 19.2 [RESOLVED in v1.2.2] `file:line='?'` on downstream terminal sinks
3081
+
3082
+ **Measurement.** The #45.1 fix rebinds a result's location from its **outgoing**
3083
+ `COLUMN_LINEAGE` edge ("where is this column used as a source"). For *downstream*
3084
+ traces this means terminal sinks render `?`: a sink has no outgoing edge, so
3085
+ `min(q.start_line)`/`min(q.file_path)` are NULL. Corpus measurement: of 36,892
3086
+ distinct columns reachable as a downstream result, **29,827 (81%) are sinks → render
3087
+ `file:line='?'`**. The fix populates only the locatable 19%. Upstream is unaffected
3088
+ (an upstream source legitimately has an outgoing edge). The TC6 guard asserts non-null
3089
+ `file:line` on one specific *located* case, so it passed despite the 81%.
3090
+
3091
+ **Decision: KEEP — pick up next.** This is a genuine bug, **not** deliberate
3092
+ semantics. Analysis tied to #45.1's own intent:
3093
+ - #45.1 chose the outgoing edge so a *source*'s location reads as "where it is
3094
+ produced/consumed as a source" — correct for the **upstream** direction. The same
3095
+ pattern was copied verbatim into the **downstream** query
3096
+ ([`analyze.py`](src/sqlcg/cli/commands/analyze.py) lines 134–137, fallback 146–149):
3097
+ `OPTIONAL MATCH (dst)-[dstedge]->()`. For a downstream *result*, the natural,
3098
+ user-meaningful location is the query that **produced** the column — i.e. the
3099
+ **incoming** edge `()-[dstedge]->(dst)`, not the outgoing one.
3100
+ - **Key lever (confirmed in code):** every one of the 36,892 columns has an incoming
3101
+ `COLUMN_LINEAGE` edge by definition — that is how it became a downstream result —
3102
+ and that producing query carries a real `start_line`/`file_path`. Flipping the two
3103
+ downstream `OPTIONAL MATCH` arrows from `(dst)-[dstedge]->()` to
3104
+ `()-[dstedge]->(dst)` binds location from the producing query, turning ~81% of `?`
3105
+ rows into a real `file:line`. Upstream stays on the outgoing edge unchanged.
3106
+
3107
+ **Risk/reward.** Reward: HIGH — ~81% of downstream result rows flip from `?` to a
3108
+ real `file:line` on a corpus this shape. Effort: LOW — a ~2-line arrow flip in two
3109
+ downstream queries (primary + bare-name fallback), no schema/index/perf change, no
3110
+ parser change. Risk: LOW and bounded — only the downstream direction; upstream and
3111
+ the kind-filter (s18.3) untouched. The one trap: TC6 passes today on a located case,
3112
+ so it does **not** catch the regression — the ticket must add a guard asserting a
3113
+ **terminal sink** (no outgoing edge) renders a real `file:line`, not `?`.
3114
+ Plan stub drafted: [`fix_downstream_sink_location.md`](plan/fix_downstream_sink_location.md).
3115
+ Suggested release: patch (1.2.2) or fold into the next lineage minor.
3116
+
3117
+ **RESOLVED — shipped in v1.2.2 (bundled with v1.2.1; see §18 bundled-release note).**
3118
+ Both downstream queries in [`analyze.py`](src/sqlcg/cli/commands/analyze.py) (primary
3119
+ L134, bare-name fallback L146) now bind location from the **incoming**
3120
+ `()-[dstedge:COLUMN_LINEAGE]->(dst)` edge — the producing query — so terminal sinks
3121
+ carry a real `file:line`. Upstream (L77/L89) keeps the outgoing-edge binding, unchanged.
3122
+ Guarded by the TC6b terminal-sink test in
3123
+ [`test_user_surface_recall_guard.py`](tests/integration/test_user_surface_recall_guard.py),
3124
+ demonstrably RED on master before the fix.
3125
+
3126
+ ### 19.3 Cross-finding note
3127
+
3128
+ Both findings share the v1.2.1 root theme: the *graph store* is correct (edges intact,
3129
+ kinds mostly right); the defects live in the **graph→user presentation layer**
3130
+ (filter classification for 19.1, location binding for 19.2) — the same blind-spot
3131
+ class as #38/#40 where anchors traversing raw edges pass while the user-facing query
3132
+ path is wrong. Any guard for either must drive the **user-facing `analyze` path**,
3133
+ not raw `COLUMN_LINEAGE` traversal.
3134
+
3135
+ ### 19.4 [KEEP — LOW] TC6b guard reconstructs the query instead of running `analyze downstream`
3136
+
3137
+ Raised by the plan-reviewer on the §19.2 sink fix. The TC6b terminal-sink guard's helper
3138
+ `_downstream_filtered_query()` in
3139
+ [`test_user_surface_recall_guard.py`](tests/integration/test_user_surface_recall_guard.py)
3140
+ **reconstructs the downstream Cypher query string** rather than invoking
3141
+ `analyze.downstream()` end-to-end. It therefore can drift from the production query if
3142
+ [`analyze.py`](src/sqlcg/cli/commands/analyze.py) changes and the helper is not updated in
3143
+ lockstep — exactly the §19.3 blind-spot (a guard not driving the true user-facing path).
3144
+ The fix held for v1.2.2 (the helper mirrors the shipped query), but the divergence risk is
3145
+ structural. **Future hardening:** replace the string-reconstruction helper with a
3146
+ `CliRunner`-based guard that runs the real `analyze downstream` command and asserts the
3147
+ rendered output. LOW priority; below §19.1 and the open §3.x items. No plan stub drafted.
@@ -59,6 +59,27 @@ This project uses `uv`. Never activate a virtualenv manually, never use `pip`, n
59
59
  | Lint | `rtk uv run ruff check src tests` |
60
60
  | Format | `uv run ruff format src tests` |
61
61
 
62
+ ## Releasing (version bump + tag)
63
+
64
+ Every feature/fix PR that ships bumps the version; after it merges to `master`, tag the release.
65
+ Do **all** of this — the tag is the step most easily forgotten.
66
+
67
+ 1. **Bump the version** (in the feature branch, before the PR merges):
68
+ - [`pyproject.toml`](pyproject.toml) `version = "X.Y.Z"`
69
+ - [`src/sqlcg/__init__.py`](src/sqlcg/__init__.py) `__version__ = "X.Y.Z"`
70
+ - `uv lock` (refreshes the lockfile's own version entry)
71
+ - **SemVer**: new capability/surface → **minor** (`1.1.x` → `1.2.0`); bug fix only → **patch**.
72
+ No backward-compat shims, so an additive feature is still minor (not major) when nothing breaks.
73
+ 2. **Merge the PR to `master`** (squash or `--merge` per the existing `Merge #NN` history).
74
+ 3. **Tag the merge commit on `master`** — **annotated**, `v`-prefixed, message `vX.Y.Z — <one-line summary>`:
75
+ ```bash
76
+ git checkout master && git pull --ff-only origin master
77
+ git tag -a vX.Y.Z -m "vX.Y.Z — <summary>" <merge-commit-sha>
78
+ git push origin vX.Y.Z
79
+ ```
80
+ All existing tags are annotated and look like `v1.1.3` (never bare `1.1.3`). Confirm with
81
+ `git tag -l --format='%(contents)' vX.Y.Z` and verify it points at the master merge commit.
82
+
62
83
  ## Target scale — serve both ends of the spectrum
63
84
 
64
85
  This project must work correctly and comfortably at two very different scales:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-code-graph
3
- Version: 1.1.3
3
+ Version: 1.2.2
4
4
  Summary: SQL code graph analyzer and lineage tracer
5
5
  Project-URL: Homepage, https://github.com/Warhorze/sql-code-graph
6
6
  Project-URL: Repository, https://github.com/Warhorze/sql-code-graph
@@ -267,6 +267,16 @@ sqlcg mcp restart # stop the server (client must respawn it
267
267
  sqlcg version # show installed version
268
268
  ```
269
269
 
270
+ ### Reads while the server is running (v1.2.0)
271
+
272
+ KuzuDB allows a single writer, so while the MCP server is live it holds the
273
+ database lock. CLI **read** commands (`find`, `analyze`, `db info`, `list-repos`,
274
+ `gain`) automatically route their query through the running server over its
275
+ control socket and return rows as usual — no flag, no config. When no server is
276
+ running they open the database directly, exactly as before. If the server is
277
+ mid-reindex the read waits for it to finish rather than failing with
278
+ "Database is locked".
279
+
270
280
  ## Supported dialects
271
281
 
272
282
  sqlcg is built on [sqlglot](https://github.com/tobymao/sqlglot), so other dialects
@@ -229,6 +229,16 @@ sqlcg mcp restart # stop the server (client must respawn it
229
229
  sqlcg version # show installed version
230
230
  ```
231
231
 
232
+ ### Reads while the server is running (v1.2.0)
233
+
234
+ KuzuDB allows a single writer, so while the MCP server is live it holds the
235
+ database lock. CLI **read** commands (`find`, `analyze`, `db info`, `list-repos`,
236
+ `gain`) automatically route their query through the running server over its
237
+ control socket and return rows as usual — no flag, no config. When no server is
238
+ running they open the database directly, exactly as before. If the server is
239
+ mid-reindex the read waits for it to finish rather than failing with
240
+ "Database is locked".
241
+
232
242
  ## Supported dialects
233
243
 
234
244
  sqlcg is built on [sqlglot](https://github.com/tobymao/sqlglot), so other dialects
@@ -17,7 +17,7 @@ bash scripts/generate_cli_docs.sh
17
17
  | `watch` | Watch a directory and re-index on SQL file changes. |
18
18
  | `gain` | Show metrics and feedback analytics. |
19
19
  | `report` | Generate a metrics report with FP clusters and parse error patterns. |
20
- | `install` | Register sqlcg as an MCP server in Claude Code (~/.claude/settings.json). |
20
+ | `install` | Register sqlcg as an MCP server in Claude Code. |
21
21
  | `uninstall` | Uninstall sqlcg from Claude Code and optionally clean up resources. |
22
22
  | `version` | Show version. |
23
23
  | `db` | Database management commands |
@@ -71,13 +71,15 @@ Schema aliases (staging schema → canonical schema) can be configured in
71
71
  | --- | --- | --- | --- | --- | --- |
72
72
  | --dialect, -d | TEXT | No | No | | SQL dialect (or 'auto' to read from .sqlcg.toml) |
73
73
  | --dbt-manifest | PATH | No | No | | Path to dbt manifest |
74
- | --timeout-per-file | INTEGER | No | No | 5 | Timeout per file in seconds |
74
+ | --timeout-per-file | INTEGER | No | No | 10 | Timeout per file in seconds |
75
75
  | --buffer-pool-size | INTEGER | No | No | 0 | KuzuDB buffer pool size in MB (0 = default). Set to 256-512 on memory-constrained machines. |
76
76
  | --batch-size | INTEGER | No | No | 50 | Files per KuzuDB transaction in the upsert pass. Default 50 balances commit-overhead reduction (vs. legacy per-file commits) against per-batch memory cost. Lower values are safer for memory-constrained machines; higher values give marginal speedup at the cost of larger working sets. Set to 1 to reproduce legacy per-file commit behaviour. |
77
77
  | --no-ddl | BOOLEAN | No | No | False | Skip table-node upserts for DDL-only files |
78
78
  | --quiet, -q | BOOLEAN | No | No | False | Suppress summary console output |
79
+ | --verbose, -v | BOOLEAN | No | No | False | Print parse warnings to stderr instead of log file |
79
80
  | --debug | BOOLEAN | No | No | False | Show detailed log output during indexing |
80
81
  | --profile / --no-profile | BOOLEAN | No | No | False | Emit per-stage timing after indexing |
82
+ | --include-working-tree | BOOLEAN | No | No | False | Index the working tree including uncommitted changes. Marks freshness as 'indexed with working-tree changes'. |
81
83
 
82
84
  ## `sqlcg reindex`
83
85
 
@@ -106,7 +108,8 @@ build — run 'sqlcg db reset && sqlcg db init && sqlcg index <path>' to re-init
106
108
  | --dialect, -d | TEXT | No | No | | SQL dialect (or 'auto' to read from .sqlcg.toml) |
107
109
  | --quiet, -q | BOOLEAN | No | No | False | Suppress summary output |
108
110
  | --batch-size | INTEGER | No | No | 50 | Files per KuzuDB transaction (same default as index command) |
109
- | --timeout-per-file | INTEGER | No | No | 5 | Per-file parse timeout in seconds |
111
+ | --timeout-per-file | INTEGER | No | No | 10 | Per-file parse timeout in seconds |
112
+ | --notify | BOOLEAN | No | No | False | If a server is live on this DB, route the reindex through the server (avoids lock contention). Falls back to direct write if no server is found. |
110
113
 
111
114
  ## `sqlcg watch`
112
115
 
@@ -181,7 +184,10 @@ If no metrics database exists, prints a message and exits 0.
181
184
  sqlcg install [OPTIONS]
182
185
  ```
183
186
 
184
- Register sqlcg as an MCP server in Claude Code (~/.claude/settings.json).
187
+ Register sqlcg as an MCP server in Claude Code.
188
+
189
+ Runs ``claude mcp add -s user sql-code-graph <cmd> <args>`` when the
190
+ ``claude`` CLI is on PATH; otherwise writes ~/.claude.json directly.
185
191
 
186
192
  Also provisions a Claude skill file (SKILL.md) at the chosen location.
187
193
  Pass --scope project or --scope global to specify where the skill is written.
@@ -333,7 +339,7 @@ Find a table by name.
333
339
 
334
340
  | Option | Type | Required | Repeatable | Default | Description |
335
341
  | --- | --- | --- | --- | --- | --- |
336
- | _none_ | | | | | |
342
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
337
343
 
338
344
  ## `sqlcg find column`
339
345
 
@@ -347,7 +353,7 @@ Find a column by table.column reference.
347
353
 
348
354
  | Option | Type | Required | Repeatable | Default | Description |
349
355
  | --- | --- | --- | --- | --- | --- |
350
- | _none_ | | | | | |
356
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
351
357
 
352
358
  ## `sqlcg find pattern`
353
359
 
@@ -394,6 +400,8 @@ Trace upstream column lineage.
394
400
  | Option | Type | Required | Repeatable | Default | Description |
395
401
  | --- | --- | --- | --- | --- | --- |
396
402
  | --depth | INTEGER | No | No | 5 | Maximum traversal depth |
403
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
404
+ | --include-intermediate | BOOLEAN | No | No | False | Include CTE/derived intermediate nodes |
397
405
 
398
406
  ## `sqlcg analyze downstream`
399
407
 
@@ -408,6 +416,8 @@ Trace downstream column lineage.
408
416
  | Option | Type | Required | Repeatable | Default | Description |
409
417
  | --- | --- | --- | --- | --- | --- |
410
418
  | --depth | INTEGER | No | No | 5 | Maximum traversal depth |
419
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
420
+ | --include-intermediate | BOOLEAN | No | No | False | Include CTE/derived intermediate nodes |
411
421
 
412
422
  ## `sqlcg analyze impact`
413
423
 
@@ -421,7 +431,7 @@ Show all queries impacted by a table.
421
431
 
422
432
  | Option | Type | Required | Repeatable | Default | Description |
423
433
  | --- | --- | --- | --- | --- | --- |
424
- | _none_ | | | | | |
434
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
425
435
 
426
436
  ## `sqlcg analyze failures`
427
437
 
@@ -431,6 +441,9 @@ sqlcg analyze failures [OPTIONS]
431
441
 
432
442
  List files that failed to parse, with their dominant cause (E-code bucket).
433
443
 
444
+ Valid --cause buckets (from highest to lowest severity):
445
+ timeout, E8, E3, E2, E5, E1, qualify_failed, func_fallback, pure_ddl_skip.
446
+
434
447
  Requires a graph indexed with sqlcg >= v3 (schema version 3). Re-index
435
448
  with 'sqlcg db reset && sqlcg index <path>' if the graph was built with
436
449
  an earlier version.
@@ -439,7 +452,7 @@ an earlier version.
439
452
 
440
453
  | Option | Type | Required | Repeatable | Default | Description |
441
454
  | --- | --- | --- | --- | --- | --- |
442
- | --cause | TEXT | No | No | | Filter by E-code bucket (e.g. E5, timeout) |
455
+ | --cause | TEXT | No | No | | Filter by E-code bucket. Valid values: timeout, E8, E3, E2, E5, E1, qualify_failed, func_fallback, pure_ddl_skip |
443
456
  | --limit | INTEGER | No | No | 100 | Maximum rows to return |
444
457
 
445
458
  ## `sqlcg analyze unused`
@@ -455,6 +468,7 @@ Find tables with no query references.
455
468
  | Option | Type | Required | Repeatable | Default | Description |
456
469
  | --- | --- | --- | --- | --- | --- |
457
470
  | --threshold | INTEGER | No | No | 0 | Minimum reference count threshold |
471
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
458
472
 
459
473
  ## `sqlcg mcp`
460
474
 
@@ -471,6 +485,9 @@ MCP server commands
471
485
  | `setup` | Print or write MCP server config JSON. |
472
486
  | `start` | Start the MCP server. |
473
487
  | `best-practices` | Print MCP tool best-practices (the fact/heuristic boundary). |
488
+ | `status` | Print server status JSON (connects to control socket). |
489
+ | `stop` | Stop the running MCP server gracefully. |
490
+ | `restart` | Stop the server. The client (editor) must respawn. |
474
491
 
475
492
  ## `sqlcg mcp setup`
476
493
 
@@ -480,6 +497,11 @@ sqlcg mcp setup [OPTIONS]
480
497
 
481
498
  Print or write MCP server config JSON.
482
499
 
500
+ --print (default): print the JSON snippet for manual insertion.
501
+ --write: write to ~/.claude.json under mcpServers.user (the correct path
502
+ for Claude Code — not settings.json, which Claude Code does not read
503
+ for MCP servers).
504
+
483
505
  ### Options
484
506
 
485
507
  | Option | Type | Required | Repeatable | Default | Description |
@@ -517,6 +539,72 @@ that have not installed the skill.
517
539
  | --- | --- | --- | --- | --- | --- |
518
540
  | _none_ | | | | | |
519
541
 
542
+ ## `sqlcg mcp status`
543
+
544
+ ```bash
545
+ sqlcg mcp status [OPTIONS]
546
+ ```
547
+
548
+ Print server status JSON (connects to control socket).
549
+
550
+ Returns JSON with fields: running, pid, db_path, indexed_sha, head_sha,
551
+ stale_by_commits, connected_clients, uptime when a server is live.
552
+
553
+ When no server is found: {"running": false}.
554
+ When the PID file exists with a live process but the socket is unavailable:
555
+ {"running": true, "degraded": "socket unavailable", ...}.
556
+
557
+ R3 (stale socket): if the socket file exists but the server is not
558
+ responding (ConnectionRefusedError / FileNotFoundError), falls through
559
+ to the PID-file probe — never hangs or errors on a dead socket.
560
+
561
+ ### Options
562
+
563
+ | Option | Type | Required | Repeatable | Default | Description |
564
+ | --- | --- | --- | --- | --- | --- |
565
+ | _none_ | | | | | |
566
+
567
+ ## `sqlcg mcp stop`
568
+
569
+ ```bash
570
+ sqlcg mcp stop [OPTIONS]
571
+ ```
572
+
573
+ Stop the running MCP server gracefully.
574
+
575
+ Sends a ``stop`` op via the control socket; waits up to 5 s for the
576
+ socket file to disappear (confirming clean exit). Falls back to SIGTERM
577
+ on the PID-file PID if the socket is unavailable.
578
+
579
+ R3 (stale socket): ``ConnectionRefusedError`` / ``FileNotFoundError`` are
580
+ caught — never hangs on a dead socket.
581
+
582
+ ### Options
583
+
584
+ | Option | Type | Required | Repeatable | Default | Description |
585
+ | --- | --- | --- | --- | --- | --- |
586
+ | _none_ | | | | | |
587
+
588
+ ## `sqlcg mcp restart`
589
+
590
+ ```bash
591
+ sqlcg mcp restart [OPTIONS]
592
+ ```
593
+
594
+ Stop the server. The client (editor) must respawn.
595
+
596
+ v1.1 cannot re-parent an editor-spawned stdio process. This command
597
+ stops the current server and prints guidance for the user to restart
598
+ the MCP server via their editor's MCP configuration.
599
+
600
+ True auto-restart (re-parenting stdio) is deferred to v1.2.
601
+
602
+ ### Options
603
+
604
+ | Option | Type | Required | Repeatable | Default | Description |
605
+ | --- | --- | --- | --- | --- | --- |
606
+ | _none_ | | | | | |
607
+
520
608
  ## `sqlcg git`
521
609
 
522
610
  ```bash
@@ -542,6 +630,8 @@ Install git hooks for sqlcg integration.
542
630
  Writes a post-checkout hook that triggers incremental resync after branch switches
543
631
  and a post-merge hook that triggers resync after pulls/merges.
544
632
  Idempotent: running multiple times produces one hook entry per hook.
633
+ The hooks embed the absolute path of the installing sqlcg binary so version skew
634
+ between the installed binary and the hook command is avoided.
545
635
 
546
636
  ### Options
547
637
 
@@ -0,0 +1,119 @@
1
+ # Feature Plan: Downstream sink `file:line` recall
2
+
3
+ > Status: **plan-reviewed — READY FOR IMPLEMENTATION.**
4
+ > Source: ARCHITECTURE_REVIEW.md §19.2 (v1.2.1 live DWH re-verification, 2026-06-02).
5
+
6
+ ## Summary
7
+
8
+ `analyze downstream` renders `file:line='?'` for ~81% of result rows (29,827 of
9
+ 36,892 distinct downstream columns on the DWH corpus) because terminal sinks have no
10
+ *outgoing* `COLUMN_LINEAGE` edge to bind location from. Bind downstream-result
11
+ location from the **incoming** edge (the producing query) instead, flipping ~81% of
12
+ `?` rows to a real `file:line`.
13
+
14
+ ## Scope
15
+
16
+ ### In Scope
17
+ - The two downstream queries in [`analyze.py`](src/sqlcg/cli/commands/analyze.py)
18
+ (`downstream` primary + bare-name fallback): change the location-binding
19
+ `OPTIONAL MATCH` from `(dst)-[dstedge]->()` to `()-[dstedge:COLUMN_LINEAGE]->(dst)`.
20
+ - A behavioural guard asserting a **terminal sink** (a column with no outgoing edge)
21
+ renders a real `file:line`, not `?`.
22
+
23
+ ### Non-Goals
24
+ - The upstream queries (`<-[…]-(src)` + `(src)-[srcedge]->()`). The outgoing-edge
25
+ binding is **correct** for upstream — do not touch lines 73–94 of `analyze.py`.
26
+ - Finding 19.1 (scratch-object classification leak) — separate, LOW-priority ticket.
27
+ - Any change to the kind-filter (`_kind_filter`, s18.3), schema, indexes, or parser.
28
+ - Re-binding `q.start_line`/`q.file_path` semantics anywhere else (server tools).
29
+
30
+ ## Design
31
+
32
+ ### Query change (the load-bearing edit)
33
+
34
+ Current downstream binding ([`analyze.py`](src/sqlcg/cli/commands/analyze.py)
35
+ lines 134–137, and the bare-name fallback 146–149):
36
+
37
+ ```cypher
38
+ OPTIONAL MATCH (dst)-[dstedge:COLUMN_LINEAGE]->()
39
+ OPTIONAL MATCH (q:SqlQuery {id: dstedge.query_id})
40
+ WITH dst, min(q.start_line) AS line, min(q.file_path) AS file
41
+ ```
42
+
43
+ Change the first arrow to bind from the **producing** query (incoming edge):
44
+
45
+ ```cypher
46
+ OPTIONAL MATCH ()-[dstedge:COLUMN_LINEAGE]->(dst)
47
+ OPTIONAL MATCH (q:SqlQuery {id: dstedge.query_id})
48
+ WITH dst, min(q.start_line) AS line, min(q.file_path) AS file
49
+ ```
50
+
51
+ Rationale: every downstream result `dst` has an incoming edge by definition (that is
52
+ how it became reachable), and that edge's `query_id` points at the query that
53
+ produced the column — the user-meaningful "where is this column written" location.
54
+ A terminal sink has no *outgoing* edge (today's `?`) but always has an incoming one.
55
+
56
+ Keep `OPTIONAL MATCH` (not `MATCH`) so a `dst` whose incoming edge has a NULL/missing
57
+ `query_id` still returns a row (degrades to `?` rather than dropping the row).
58
+
59
+ ### Data Models / Dependencies
60
+ None. No schema, index, or dependency change.
61
+
62
+ ## Implementation Steps
63
+
64
+ ### Phase 1: Query rewrite
65
+ **Step 1.1**: Flip the location-binding arrow in both downstream queries.
66
+ - Files affected: [`analyze.py`](src/sqlcg/cli/commands/analyze.py) (downstream
67
+ primary ~134, bare-name fallback ~146).
68
+ - Acceptance: both queries bind `()-[dstedge]->(dst)`; upstream queries unchanged
69
+ (grep confirms `(src)-[srcedge]->()` still present at lines 77 / 89).
70
+
71
+ ### Phase 2: Guard
72
+ **Step 2.1**: Add `test_tc6b_downstream_sink_location_present` to
73
+ [`test_user_surface_recall_guard.py`](tests/integration/test_user_surface_recall_guard.py).
74
+ - The terminal sink to use from the existing A–D corpus is **`mart.fact_kpi.measure`**.
75
+ It is the final INSERT target — it has no outgoing `COLUMN_LINEAGE` edge (nothing
76
+ downstream consumes it), but it IS reachable as a downstream result from
77
+ `staging.src_a.x`. Confirm this via `MATCH (c:SqlColumn {id: 'mart.fact_kpi.measure'})-
78
+ [:COLUMN_LINEAGE]->() RETURN count(*)` returning 0 before relying on it.
79
+ - Build the downstream query using `_kind_filter("dst", include_intermediate=False)` —
80
+ the same helper `analyze downstream` calls — so the guard drives the production query
81
+ path, not raw edge traversal (ARCHITECTURE_REVIEW.md §19.3).
82
+ - Start the traversal from `staging.src_a.x` (a physical source) and assert that the
83
+ row for `mart.fact_kpi.measure` carries a non-null `file` field. Assert observable
84
+ output (non-null `file` value), not "no exception raised".
85
+ - Acceptance: the new test is RED against the current outgoing-edge query on master and
86
+ GREEN after the Step 1.1 flip. Verify by running it before and after the edit.
87
+
88
+ ## Test Strategy
89
+ - Integration: the new TC6b guard on the in-fixture sink (above).
90
+ - Manual/live (optional, recorded in plan postmortem, not committed — DWH e2e is
91
+ gitignored): re-run `analyze downstream` on a DWH column and confirm the `?` rate
92
+ drops from ~81% toward ~0% for sinks.
93
+ - Regression: full `uv run pytest` — confirm TC6 (upstream) and the kind-filter
94
+ guards (TC1/TC4) stay green; no perf/scaling guard touched (query-only change).
95
+
96
+ ## Acceptance Criteria
97
+ - [ ] Both downstream queries bind location from the incoming `COLUMN_LINEAGE` edge.
98
+ - [ ] Upstream queries are unchanged (outgoing-edge binding preserved).
99
+ - [ ] A terminal-sink downstream result renders a real `file:line` (TC6b green).
100
+ - [ ] TC6b is demonstrably RED on `master` before the fix (proves it guards the bug).
101
+ - [ ] Full test suite green; no perf/scaling guard regression.
102
+ - [ ] Version bumped to **1.2.2** (patch) per CLAUDE.md release process.
103
+ **Bundling decision (2026-06-02, supersedes the original standalone plan):** this
104
+ sink fix and the v1.2.1 bugfix scope ship together as a single **v1.2.2** release;
105
+ the lower `v1.2.1` tag is **skipped** (precedent: v1.1.2→v1.1.3). The bundled work
106
+ lives on `feat/v1.2.1-bugfix` (PR #48, fast-forwarded to `b5bf7a3`). The combined
107
+ plan-compliance verdict is recorded in
108
+ [`v1_2_1_bugfix.md`](v1_2_1_bugfix.md) under "Bundled v1.2.2 release compliance".
109
+
110
+ ## Risks and Mitigations
111
+ - **Risk: a `dst` with multiple incoming edges from different queries.** `min()`
112
+ aggregation already collapses fanout (same pattern as #45.1); pick the
113
+ lowest-line producing query deterministically. Mitigation: keep the
114
+ `WITH dst, min(...), min(...)` aggregate-before-`LIMIT` shape exactly as upstream.
115
+ - **Risk: TC6 false-confidence (it passes on a located case).** Mitigation: Phase 2
116
+ explicitly targets a *sink* — `mart.fact_kpi.measure` with zero outgoing edges —
117
+ the case TC6 misses; prove RED-on-master before the fix.
118
+ - **Risk: scope creep into server tools.** Out of scope — this ticket is the CLI
119
+ `analyze downstream` path only.