sql-code-graph 1.4.3__tar.gz → 1.5.1__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 (444) hide show
  1. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/PKG-INFO +21 -8
  2. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/README.md +20 -7
  3. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/docs/cli.md +18 -10
  4. sql_code_graph-1.5.1/plan/version-parity-and-restart.md +390 -0
  5. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/pyproject.toml +1 -1
  6. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/__init__.py +1 -1
  7. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/install.py +56 -43
  8. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/mcp.py +58 -10
  9. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/main.py +34 -3
  10. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/models.py +3 -0
  11. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/server.py +9 -0
  12. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/tools.py +11 -1
  13. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_install.py +80 -0
  14. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_mcp_control.py +130 -0
  15. sql_code_graph-1.5.1/tests/unit/test_version_parity.py +148 -0
  16. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/uv.lock +1 -1
  17. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/api-documenter.md +0 -0
  18. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/architect-planner.md +0 -0
  19. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/architect-reviewer.md +0 -0
  20. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/code-reviewer.md +0 -0
  21. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/developer.md +0 -0
  22. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/plan-reviewer.md +0 -0
  23. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/sprint-planner.md +0 -0
  24. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  25. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  26. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  27. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/benchmark.yml +0 -0
  28. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/e2e-tests.yml +0 -0
  29. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/release.yml +0 -0
  30. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/test.yml +0 -0
  31. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.gitignore +0 -0
  32. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.pre-commit-config.yaml +0 -0
  33. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.sqlcgignore +0 -0
  34. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/ARCHITECTURE_REVIEW.md +0 -0
  35. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/CHANGELOG.md +0 -0
  36. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/CLAUDE.md +0 -0
  37. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/docs/AIRBNB_PARSE_REPORT.md +0 -0
  38. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/e2e_firstuser_report.md +0 -0
  39. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/e2e_run_20260528_101413.output +0 -0
  40. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/main.py +0 -0
  41. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/WORKFLOW.md +0 -0
  42. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/blueprint.md +0 -0
  43. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/bundle_claude_skill.md +0 -0
  44. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/diff-impact-producer-file.md +0 -0
  45. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_34_unused_presentation_segregation.md +0 -0
  46. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_35_external_downstream_injection.md +0 -0
  47. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_F2_bundle_claude_skill.md +0 -0
  48. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_kuzu_to_duckdb_migration.md +0 -0
  49. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_downstream_sink_location.md +0 -0
  50. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_dynamic_table_parsing.md +0 -0
  51. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_expand_qualify_perf.md +0 -0
  52. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_firstuser_findings.md +0 -0
  53. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_issue29_live_test_followups.md +0 -0
  54. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_schema_case_mismatch.md +0 -0
  55. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/hygiene_config_path_and_survivors.md +0 -0
  56. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/investigation_e5_e4.md +0 -0
  57. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/issue-38-backfill-cte-bridge.md +0 -0
  58. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/living_codebase_resync.md +0 -0
  59. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/schema_comparison_with_schema.json +0 -0
  60. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/schema_comparison_without_schema.json +0 -0
  61. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/sprint_08_changelogs_fullindex.json +0 -0
  62. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/sprint_08_fullcorpus_index.json +0 -0
  63. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/sprint_pool_300s_plan.json +0 -0
  64. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/parse_diagnostics.md +0 -0
  65. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/parsing_errors_experiment.md +0 -0
  66. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/snowflake_en_test_suite.md +0 -0
  67. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_01.md +0 -0
  68. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_01_deployment_pypi.md +0 -0
  69. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_02.md +0 -0
  70. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_02_v0.3.0_core.md +0 -0
  71. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_03.md +0 -0
  72. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_03_v0.3.1_postmortem.md +0 -0
  73. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_04_column_lineage.md +0 -0
  74. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_04_column_lineage_fix.md +0 -0
  75. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_05_star_resolution.md +0 -0
  76. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_06_lineage_coverage.md +0 -0
  77. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_07_open_ecodes.md +0 -0
  78. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_07_perf_and_live_test.md +0 -0
  79. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_08_perf_upsert.md +0 -0
  80. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_09_lineage_coverage.md +0 -0
  81. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_10_anchor_tools.md +0 -0
  82. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_11_v1.0.2_bugfix.md +0 -0
  83. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_12_v1.1.0.md +0 -0
  84. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_13_v1.1.0_cluster_b.md +0 -0
  85. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_3.1_postmortem.md +0 -0
  86. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sqlcg.md +0 -0
  87. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/trust_layer.md +0 -0
  88. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1.1.0_cluster_b_provenance_trust.md +0 -0
  89. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1.1.0_live_graph_freshness.md +0 -0
  90. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1.1.1_batch_upsert_perf.md +0 -0
  91. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_1_2_bugfix.md +0 -0
  92. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_1_3_union_cte_star.md +0 -0
  93. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_2_0_read_proxy.md +0 -0
  94. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_2_1_bugfix.md +0 -0
  95. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/profile.html +0 -0
  96. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/pyrightconfig.json +0 -0
  97. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/scripts/collect_parse_errors.py +0 -0
  98. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/scripts/generate_cli_docs.sh +0 -0
  99. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/__main__.py +0 -0
  100. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/__init__.py +0 -0
  101. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/__init__.py +0 -0
  102. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/analyze.py +0 -0
  103. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/db.py +0 -0
  104. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/find.py +0 -0
  105. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/gain.py +0 -0
  106. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/git.py +0 -0
  107. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/index.py +0 -0
  108. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/reindex.py +0 -0
  109. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/report.py +0 -0
  110. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/uninstall.py +0 -0
  111. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/watch.py +0 -0
  112. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/__init__.py +0 -0
  113. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/config.py +0 -0
  114. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/duckdb_backend.py +0 -0
  115. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/freshness.py +0 -0
  116. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/graph_db.py +0 -0
  117. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/jobs.py +0 -0
  118. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/queries.cypher +0 -0
  119. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/queries.py +0 -0
  120. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/queries.sql +0 -0
  121. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/schema.cypher +0 -0
  122. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/schema.py +0 -0
  123. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/__init__.py +0 -0
  124. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/dbt_adapter.py +0 -0
  125. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/error_classify.py +0 -0
  126. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/git_delta.py +0 -0
  127. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/indexer.py +0 -0
  128. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/pool.py +0 -0
  129. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/walker.py +0 -0
  130. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/watcher.py +0 -0
  131. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/lineage/__init__.py +0 -0
  132. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/lineage/aggregator.py +0 -0
  133. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/lineage/schema_resolver.py +0 -0
  134. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/metrics/__init__.py +0 -0
  135. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/metrics/store.py +0 -0
  136. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/__init__.py +0 -0
  137. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/ansi_parser.py +0 -0
  138. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/base.py +0 -0
  139. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/bigquery_parser.py +0 -0
  140. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/postgres_parser.py +0 -0
  141. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/registry.py +0 -0
  142. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/snowflake_parser.py +0 -0
  143. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/tsql_parser.py +0 -0
  144. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/__init__.py +0 -0
  145. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/control.py +0 -0
  146. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/exceptions.py +0 -0
  147. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/noise_filter.py +0 -0
  148. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/read_client.py +0 -0
  149. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/skill.py +0 -0
  150. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/writer.py +0 -0
  151. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/__init__.py +0 -0
  152. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/hashing.py +0 -0
  153. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/ignore.py +0 -0
  154. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/logging.py +0 -0
  155. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/__init__.py +0 -0
  156. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/__init__.py +0 -0
  157. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/adversarial/200_join.sql +0 -0
  158. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/adversarial/500_union.sql +0 -0
  159. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/bench_indexer.py +0 -0
  160. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/conftest.py +0 -0
  161. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/case_normalization.sql +0 -0
  162. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/colon_cast.sql +0 -0
  163. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/colon_reserved_word.sql +0 -0
  164. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/copy_into.sql +0 -0
  165. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/create_procedure.sql +0 -0
  166. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/identifier_dynamic.sql +0 -0
  167. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/lateral_flatten.sql +0 -0
  168. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/qualify.sql +0 -0
  169. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/scripting_block.sql +0 -0
  170. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/three_part.sql +0 -0
  171. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q01.sql +0 -0
  172. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q02.sql +0 -0
  173. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q03.sql +0 -0
  174. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q04.sql +0 -0
  175. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q05.sql +0 -0
  176. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/__init__.py +0 -0
  177. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/conftest.py +0 -0
  178. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_F2_skill_install_e2e.py +0 -0
  179. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_airbnb_e2e.py +0 -0
  180. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_cli_index.py +0 -0
  181. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_git_hook_install.py +0 -0
  182. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_golden_lineage.py +0 -0
  183. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_mcp_lifecycle.py +0 -0
  184. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_mcp_tools.py +0 -0
  185. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_parse_diagnostics_cli.py +0 -0
  186. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_star_resolution_e2e.py +0 -0
  187. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_watch.py +0 -0
  188. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/dim_hosts_cleansed.sql +0 -0
  189. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/dim_listings_cleansed.sql +0 -0
  190. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/fct_reviews.sql +0 -0
  191. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/mart_fullmoon_reviews.sql +0 -0
  192. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/raw_hosts.sql +0 -0
  193. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/raw_listings.sql +0 -0
  194. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/raw_reviews.sql +0 -0
  195. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/src_hosts.sql +0 -0
  196. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/src_listings.sql +0 -0
  197. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/src_reviews.sql +0 -0
  198. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/bigquery/.gitkeep +0 -0
  199. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/duckdb_parity/kuzu_reference.json +0 -0
  200. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/jaffle_shop/customers.sql +0 -0
  201. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/jaffle_shop/orders.sql +0 -0
  202. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/jaffle_shop/raw_orders.sql +0 -0
  203. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/snowflake/base_tables.sql +0 -0
  204. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/snowflake/reports.sql +0 -0
  205. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/snowflake/views.sql +0 -0
  206. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/ddl_src.sql +0 -0
  207. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/ddl_tgt.sql +0 -0
  208. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/etl_alias_star.sql +0 -0
  209. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/etl_star.sql +0 -0
  210. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/synthetic/base_tables.sql +0 -0
  211. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/synthetic/reports.sql +0 -0
  212. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/synthetic/views.sql +0 -0
  213. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/__init__.py +0 -0
  214. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/snowflake/__init__.py +0 -0
  215. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/snowflake/test_insert_select.py +0 -0
  216. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_T34_presentation_segregation.py +0 -0
  217. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_T35_external_consumers.py +0 -0
  218. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_analyze_case_fold.py +0 -0
  219. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_anchor_tools.py +0 -0
  220. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_backfill_impact_consistency.py +0 -0
  221. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_bare_column_cte_lineage.py +0 -0
  222. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_bulk_upsert.py +0 -0
  223. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_case_split_seed_regression.py +0 -0
  224. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_column_lineage_e2e.py +0 -0
  225. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cross_file_lineage.py +0 -0
  226. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cte_recall_guard.py +0 -0
  227. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cte_schema_alias_guard.py +0 -0
  228. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cte_source_node_invariant.py +0 -0
  229. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_dialect_auto_resolution.py +0 -0
  230. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_dialect_matrix.py +0 -0
  231. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_duckdb_parity.py +0 -0
  232. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_e36_xfile_regression_guard.py +0 -0
  233. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_freshness_mcp.py +0 -0
  234. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_hygiene_config_root_reconciliation.py +0 -0
  235. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_indexer_batching.py +0 -0
  236. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_indexer_commits.py +0 -0
  237. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_indexer_to_graph.py +0 -0
  238. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_live_anchors.py +0 -0
  239. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_mvcc_rebuild.py +0 -0
  240. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_parse_diagnostics.py +0 -0
  241. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_pr1_confidence_reason.py +0 -0
  242. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_pr2_source_location.py +0 -0
  243. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_pr3_kind_tagging.py +0 -0
  244. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_read_via_server.py +0 -0
  245. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_readonly_under_lock.py +0 -0
  246. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_reindex_via_server.py +0 -0
  247. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_resync.py +0 -0
  248. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_single_writer_queue.py +0 -0
  249. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_star_resolution.py +0 -0
  250. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_temp_table_lineage.py +0 -0
  251. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_union_cte_star_recall_guard.py +0 -0
  252. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_user_surface_recall_guard.py +0 -0
  253. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_v141_surface_guards.py +0 -0
  254. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/perf/__init__.py +0 -0
  255. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/perf/test_perf.py +0 -0
  256. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E10/__init__.py +0 -0
  257. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E11/__init__.py +0 -0
  258. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/__init__.py +0 -0
  259. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/e12_json_path.sql +0 -0
  260. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/e12_lateral_flatten.sql +0 -0
  261. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/test_e12.py +0 -0
  262. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E13/__init__.py +0 -0
  263. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E14/__init__.py +0 -0
  264. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E15/__init__.py +0 -0
  265. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/__init__.py +0 -0
  266. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/e16_merge.sql +0 -0
  267. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/e16_merge_delete.sql +0 -0
  268. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/test_e16.py +0 -0
  269. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E17/__init__.py +0 -0
  270. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/__init__.py +0 -0
  271. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/e18_decode.sql +0 -0
  272. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/e18_iff_decode.sql +0 -0
  273. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/e18_nvl2.sql +0 -0
  274. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/test_e18.py +0 -0
  275. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E19/__init__.py +0 -0
  276. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/__init__.py +0 -0
  277. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/e2_expr_alias.sql +0 -0
  278. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/e2_function_alias.sql +0 -0
  279. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/e2_multiply_alias.sql +0 -0
  280. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/test_e2.py +0 -0
  281. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E20/__init__.py +0 -0
  282. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/__init__.py +0 -0
  283. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/e21_alias_forward_ref.sql +0 -0
  284. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/e21_three_level_chain.sql +0 -0
  285. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/test_e21.py +0 -0
  286. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E22/__init__.py +0 -0
  287. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/__init__.py +0 -0
  288. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/e23_stored_proc.sql +0 -0
  289. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/e23_stored_proc_multi.sql +0 -0
  290. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/test_e23.py +0 -0
  291. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E24/__init__.py +0 -0
  292. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/__init__.py +0 -0
  293. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/e25_cross_db.sql +0 -0
  294. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/e25_two_part.sql +0 -0
  295. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/test_e25.py +0 -0
  296. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/test_e25_full_id.py +0 -0
  297. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E26/__init__.py +0 -0
  298. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/__init__.py +0 -0
  299. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/e27_nested_udf.sql +0 -0
  300. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/e27_udf.sql +0 -0
  301. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/test_e27.py +0 -0
  302. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E28/__init__.py +0 -0
  303. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E29/__init__.py +0 -0
  304. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/__init__.py +0 -0
  305. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/e3_alter_table.sql +0 -0
  306. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/e3_create_sequence.sql +0 -0
  307. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/e3_ddl_only.sql +0 -0
  308. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/test_e3.py +0 -0
  309. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E30/__init__.py +0 -0
  310. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E31/__init__.py +0 -0
  311. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E32/__init__.py +0 -0
  312. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E33/__init__.py +0 -0
  313. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E34/__init__.py +0 -0
  314. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E35/__init__.py +0 -0
  315. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/__init__.py +0 -0
  316. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/e36_temp_multi_use.sql +0 -0
  317. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/e36_temp_table.sql +0 -0
  318. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/test_e36.py +0 -0
  319. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/test_e36_xfile.py +0 -0
  320. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E37/__init__.py +0 -0
  321. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E38/__init__.py +0 -0
  322. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/__init__.py +0 -0
  323. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_execute_immediate.sql +0 -0
  324. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_if_not_exists.sql +0 -0
  325. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_unexpected_token.sql +0 -0
  326. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_unpivot.sql +0 -0
  327. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/test_e4.py +0 -0
  328. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/__init__.py +0 -0
  329. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/e5_cte_missing_source.sql +0 -0
  330. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/e5_multi_cte.sql +0 -0
  331. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/e5_nested_cte.sql +0 -0
  332. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/test_e5.py +0 -0
  333. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/test_e5_cte_projection.py +0 -0
  334. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/__init__.py +0 -0
  335. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/e8_dynamic_sources.sql +0 -0
  336. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/e8_seq_nextval.sql +0 -0
  337. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/e8_uuid.sql +0 -0
  338. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/test_e8.py +0 -0
  339. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E9/__init__.py +0 -0
  340. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/__init__.py +0 -0
  341. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/fixture_sum_absent_cross_schema.sql +0 -0
  342. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/fixture_sum_case_when.sql +0 -0
  343. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/fixture_sum_present_source.sql +0 -0
  344. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/test_e_aggregates.py +0 -0
  345. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/__init__.py +0 -0
  346. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_date_aliased.sql +0 -0
  347. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_date_unaliased.sql +0 -0
  348. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_datediff_unaliased.sql +0 -0
  349. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_year_unaliased.sql +0 -0
  350. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/test_e_date_functions.py +0 -0
  351. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/README.md +0 -0
  352. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/__init__.py +0 -0
  353. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/__init__.py +0 -0
  354. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_etl.sql +0 -0
  355. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_omloopsnelheid.sql +0 -0
  356. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_semantic.sql +0 -0
  357. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_source.sql +0 -0
  358. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/test_anchor_ma_aantal_op_order.py +0 -0
  359. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/test_anchor_omloopsnelheid.py +0 -0
  360. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/conftest.py +0 -0
  361. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/test_plan_review_gates.py +0 -0
  362. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/__init__.py +0 -0
  363. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/snowflake/__init__.py +0 -0
  364. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/snowflake/test_scripting_noise.py +0 -0
  365. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_BugB_escalation_uses_init_path.py +0 -0
  366. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_BugC_hook_upgrade.py +0 -0
  367. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_F2_install_skill.py +0 -0
  368. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_F2_skill_render.py +0 -0
  369. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_F2_uninstall_skill.py +0 -0
  370. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_01_qualify_once.py +0 -0
  371. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_02_ddl_skip.py +0 -0
  372. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_04_subprocess_isolate.py +0 -0
  373. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_06_log_verbosity.py +0 -0
  374. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T35_config_external_consumers.py +0 -0
  375. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_aggregator.py +0 -0
  376. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_aggregator_skip.py +0 -0
  377. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_analyze_case_fold.py +0 -0
  378. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_base_parser.py +0 -0
  379. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_branch_monitor.py +0 -0
  380. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_bulk_upsert_invariant.py +0 -0
  381. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_canonical_target_resolution.py +0 -0
  382. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_cli.py +0 -0
  383. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_cli_help.py +0 -0
  384. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_closure_depth.py +0 -0
  385. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_column_lineage_wiring.py +0 -0
  386. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_config.py +0 -0
  387. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_data_models.py +0 -0
  388. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_db_info.py +0 -0
  389. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_dominant_cause.py +0 -0
  390. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_duckdb_backend.py +0 -0
  391. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_duckdb_backend_shared.py +0 -0
  392. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_find_cmd.py +0 -0
  393. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_firstuser_findings.py +0 -0
  394. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_freshness_helper.py +0 -0
  395. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_gain_ratio.py +0 -0
  396. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_git_delta.py +0 -0
  397. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_git_hooks.py +0 -0
  398. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_git_hooks_notify.py +0 -0
  399. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_graph_backend.py +0 -0
  400. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_graph_completeness_invariant.py +0 -0
  401. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_hard_kill_pool.py +0 -0
  402. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_hygiene_config_warning.py +0 -0
  403. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_include_working_tree.py +0 -0
  404. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_index_cmd.py +0 -0
  405. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_index_flags.py +0 -0
  406. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_index_progress.py +0 -0
  407. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_indexer_progress.py +0 -0
  408. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_indexer_quality.py +0 -0
  409. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_install_message.py +0 -0
  410. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_jobs.py +0 -0
  411. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_judgement.py +0 -0
  412. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_lineage_conversion.py +0 -0
  413. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_literal_column_skip.py +0 -0
  414. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_mcp_best_practices.py +0 -0
  415. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_mcp_stdio_smoke.py +0 -0
  416. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_metrics.py +0 -0
  417. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_noise_filter.py +0 -0
  418. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_parse_file_dependency_filter.py +0 -0
  419. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_parse_quality.py +0 -0
  420. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_parser.py +0 -0
  421. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_perf_scaling_guard.py +0 -0
  422. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_pr07_observability.py +0 -0
  423. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_queries_loader.py +0 -0
  424. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_read_client.py +0 -0
  425. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_resolve_pass2_passes_dependency_filter.py +0 -0
  426. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_schema_resolver.py +0 -0
  427. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_server.py +0 -0
  428. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_snowflake_strip_alter_set_tag.py +0 -0
  429. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_sprint_06_t04_t05.py +0 -0
  430. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_star_resolution_unit.py +0 -0
  431. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_star_schema_unit.py +0 -0
  432. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_submit_feedback.py +0 -0
  433. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_subprocess_isolate.py +0 -0
  434. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_t02_expression_name_extraction.py +0 -0
  435. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_t03_ddl_skip.py +0 -0
  436. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_timeout_cancel.py +0 -0
  437. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_tools_hints.py +0 -0
  438. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_tools_warnings.py +0 -0
  439. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_uninstall.py +0 -0
  440. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_unqualified_fallback.py +0 -0
  441. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_upsert_batch_invariant.py +0 -0
  442. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_walker.py +0 -0
  443. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_watcher.py +0 -0
  444. {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_writer_queue.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-code-graph
3
- Version: 1.4.3
3
+ Version: 1.5.1
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
@@ -36,7 +36,7 @@ Description-Content-Type: text/markdown
36
36
 
37
37
  # sql-code-graph
38
38
 
39
- > **Pre-1.0 — expect breaking changes.** APIs, CLI flags, and graph schema may
39
+ > **No backward-compatibility guarantees.** APIs, CLI flags, and graph schema may
40
40
  > change between releases without a deprecation period. Pin to an exact version
41
41
  > in production. Re-indexing is always the migration path.
42
42
 
@@ -93,7 +93,7 @@ for that project.
93
93
  # 1. Install
94
94
  pip install sql-code-graph
95
95
 
96
- # 2. Register with Claude Code (~/.claude/settings.json)
96
+ # 2. Register with Claude Code (writes ~/.claude.json)
97
97
  sqlcg install
98
98
 
99
99
  # 3. Restart Claude Code
@@ -138,7 +138,7 @@ This project uses sql-code-graph. MCP tools are available:
138
138
  - `trace_column_lineage` — trace where a column's value comes from
139
139
  - `get_upstream_dependencies` / `get_downstream_dependencies` — dependency chains
140
140
  - `search_sql_pattern` — full-text search across all indexed SQL
141
- - `execute_cypher` — raw graph query for advanced analysis
141
+ - `execute_sql` — raw read-only SQL query for advanced analysis
142
142
  ```
143
143
 
144
144
  The MCP server works without this — Claude can discover the tools on its own —
@@ -214,11 +214,14 @@ After indexing, `sqlcg db info` shows non-zero `STAR_EXPANSION lineage edges`, a
214
214
  | `diff_impact(changed_files)` | What a set of changed files affects downstream |
215
215
  | `get_backfill_order(table_qualified)` | Topological rebuild/backfill order |
216
216
  | `scope_change(target)` | Synthesised change-scope summary for a target |
217
+ | **Analysis** | |
218
+ | `get_hub_ranking(k)` | Top-k tables by downstream dependent count (hub/centrality) |
219
+ | `analyze_unused()` | Tables with no within-corpus consumers (dead-code candidates, heuristic) |
217
220
  | **Search & meta** | |
218
221
  | `search_sql_pattern(query)` | Full-text search across indexed SQL |
219
222
  | `list_dialects_and_repos()` | List indexed repos and dialects (catalogue) |
220
223
  | `db_info()` | Graph health, node counts, parse quality breakdown, warnings, freshness (indexed SHA vs HEAD) |
221
- | `execute_cypher(query)` | Raw Cypher query against the graph |
224
+ | `execute_sql(query)` | Raw read-only SQL query against the graph |
222
225
  | `submit_feedback(...)` | Report a false positive/negative to improve metrics |
223
226
 
224
227
  > **Input format**: lineage/dependency tools expect a **schema-qualified** column
@@ -259,16 +262,26 @@ sqlcg gain # show usage metrics
259
262
  sqlcg report # generate FP/error report
260
263
  sqlcg mcp best-practices # print the fact/heuristic boundary for the MCP tools
261
264
  sqlcg mcp start # start MCP server manually
262
- sqlcg mcp status # server status JSON (via control socket)
265
+ sqlcg mcp status # server status JSON incl. running version + stale_by_version
263
266
  sqlcg mcp stop # stop the running MCP server gracefully
264
267
  sqlcg mcp restart # stop the server (client must respawn it)
265
268
  sqlcg version # show installed version
266
269
  ```
267
270
 
271
+ ### Staying on the latest build (v1.5.0)
272
+
273
+ The installed package, the CLI, and the running MCP server all report the **same**
274
+ version (`sqlcg.__version__`). After upgrading, an editor may still be talking to an
275
+ old MCP process — `sqlcg mcp status` surfaces this directly: it reports the running
276
+ server's `version` and a `stale_by_version` flag that is `true` when the live server
277
+ differs from the installed build. Re-running `sqlcg install` stops the stale server so
278
+ your editor respawns it on the new build, so you never debug against an outdated server.
279
+
268
280
  ### Reads while the server is running (v1.2.0)
269
281
 
270
- KuzuDB allows a single writer, so while the MCP server is live it holds the
271
- database lock. CLI **read** commands (`find`, `analyze`, `db info`, `list-repos`,
282
+ DuckDB takes an exclusive lock on the database file, so while the MCP server is
283
+ live it holds that lock (other processes cannot open the file, even read-only).
284
+ CLI **read** commands (`find`, `analyze`, `db info`, `list-repos`,
272
285
  `gain`) automatically route their query through the running server over its
273
286
  control socket and return rows as usual — no flag, no config. When no server is
274
287
  running they open the database directly, exactly as before. If the server is
@@ -1,6 +1,6 @@
1
1
  # sql-code-graph
2
2
 
3
- > **Pre-1.0 — expect breaking changes.** APIs, CLI flags, and graph schema may
3
+ > **No backward-compatibility guarantees.** APIs, CLI flags, and graph schema may
4
4
  > change between releases without a deprecation period. Pin to an exact version
5
5
  > in production. Re-indexing is always the migration path.
6
6
 
@@ -57,7 +57,7 @@ for that project.
57
57
  # 1. Install
58
58
  pip install sql-code-graph
59
59
 
60
- # 2. Register with Claude Code (~/.claude/settings.json)
60
+ # 2. Register with Claude Code (writes ~/.claude.json)
61
61
  sqlcg install
62
62
 
63
63
  # 3. Restart Claude Code
@@ -102,7 +102,7 @@ This project uses sql-code-graph. MCP tools are available:
102
102
  - `trace_column_lineage` — trace where a column's value comes from
103
103
  - `get_upstream_dependencies` / `get_downstream_dependencies` — dependency chains
104
104
  - `search_sql_pattern` — full-text search across all indexed SQL
105
- - `execute_cypher` — raw graph query for advanced analysis
105
+ - `execute_sql` — raw read-only SQL query for advanced analysis
106
106
  ```
107
107
 
108
108
  The MCP server works without this — Claude can discover the tools on its own —
@@ -178,11 +178,14 @@ After indexing, `sqlcg db info` shows non-zero `STAR_EXPANSION lineage edges`, a
178
178
  | `diff_impact(changed_files)` | What a set of changed files affects downstream |
179
179
  | `get_backfill_order(table_qualified)` | Topological rebuild/backfill order |
180
180
  | `scope_change(target)` | Synthesised change-scope summary for a target |
181
+ | **Analysis** | |
182
+ | `get_hub_ranking(k)` | Top-k tables by downstream dependent count (hub/centrality) |
183
+ | `analyze_unused()` | Tables with no within-corpus consumers (dead-code candidates, heuristic) |
181
184
  | **Search & meta** | |
182
185
  | `search_sql_pattern(query)` | Full-text search across indexed SQL |
183
186
  | `list_dialects_and_repos()` | List indexed repos and dialects (catalogue) |
184
187
  | `db_info()` | Graph health, node counts, parse quality breakdown, warnings, freshness (indexed SHA vs HEAD) |
185
- | `execute_cypher(query)` | Raw Cypher query against the graph |
188
+ | `execute_sql(query)` | Raw read-only SQL query against the graph |
186
189
  | `submit_feedback(...)` | Report a false positive/negative to improve metrics |
187
190
 
188
191
  > **Input format**: lineage/dependency tools expect a **schema-qualified** column
@@ -223,16 +226,26 @@ sqlcg gain # show usage metrics
223
226
  sqlcg report # generate FP/error report
224
227
  sqlcg mcp best-practices # print the fact/heuristic boundary for the MCP tools
225
228
  sqlcg mcp start # start MCP server manually
226
- sqlcg mcp status # server status JSON (via control socket)
229
+ sqlcg mcp status # server status JSON incl. running version + stale_by_version
227
230
  sqlcg mcp stop # stop the running MCP server gracefully
228
231
  sqlcg mcp restart # stop the server (client must respawn it)
229
232
  sqlcg version # show installed version
230
233
  ```
231
234
 
235
+ ### Staying on the latest build (v1.5.0)
236
+
237
+ The installed package, the CLI, and the running MCP server all report the **same**
238
+ version (`sqlcg.__version__`). After upgrading, an editor may still be talking to an
239
+ old MCP process — `sqlcg mcp status` surfaces this directly: it reports the running
240
+ server's `version` and a `stale_by_version` flag that is `true` when the live server
241
+ differs from the installed build. Re-running `sqlcg install` stops the stale server so
242
+ your editor respawns it on the new build, so you never debug against an outdated server.
243
+
232
244
  ### Reads while the server is running (v1.2.0)
233
245
 
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`,
246
+ DuckDB takes an exclusive lock on the database file, so while the MCP server is
247
+ live it holds that lock (other processes cannot open the file, even read-only).
248
+ CLI **read** commands (`find`, `analyze`, `db info`, `list-repos`,
236
249
  `gain`) automatically route their query through the running server over its
237
250
  control socket and return rows as usual — no flag, no config. When no server is
238
251
  running they open the database directly, exactly as before. If the server is
@@ -51,6 +51,7 @@ Note: Binary is `sqlcg`; PyPI package is `sql-code-graph`.
51
51
 
52
52
  | Option | Type | Required | Repeatable | Default | Description |
53
53
  | --- | --- | --- | --- | --- | --- |
54
+ | --version | BOOLEAN | No | No | False | Show version and exit. |
54
55
  | --install-completion | BOOLEAN | No | No | | Install completion for the current shell. |
55
56
  | --show-completion | BOOLEAN | No | No | | Show completion for the current shell, to copy it or customize the installation. |
56
57
 
@@ -547,9 +548,19 @@ sqlcg mcp status [OPTIONS]
547
548
 
548
549
  Print server status JSON (connects to control socket).
549
550
 
550
- Returns JSON with fields: running, pid, db_path, indexed_sha, head_sha,
551
- stale_by_commits, connected_clients, uptime, writer_queue when a server
552
- is live.
551
+ Returns JSON with fields: running, version, pid, db_path, indexed_sha,
552
+ head_sha, stale_by_commits, connected_clients, uptime, writer_queue,
553
+ stale_by_version when a server is live.
554
+
555
+ `version` is the *running* server's reported sqlcg.__version__ (read live
556
+ over the control socket — the build the editor is currently talking to).
557
+ `stale_by_version` compares it against the *installed* sqlcg.__version__
558
+ (the CLI process you just invoked): true when they differ, false when they
559
+ match, null when the running version cannot be determined (degraded mode).
560
+ When stale, a warning is printed telling you to restart the MCP server via
561
+ your editor or run `sqlcg install` (which stops the stale server for you).
562
+ This is distinct from `stale_by_commits` (graph-vs-repo freshness, not
563
+ package-vs-binary drift).
553
564
 
554
565
  The status response is length-prefixed framed (v1.3.0, B3) so large
555
566
  writer_queue payloads are received in full — the client uses the
@@ -557,7 +568,8 @@ recv-exactly makefile+readline+read(n) pattern, NOT a single recv(4096).
557
568
 
558
569
  When no server is found: {"running": false}.
559
570
  When the PID file exists with a live process but the socket is unavailable:
560
- {"running": true, "degraded": "socket unavailable", ...}.
571
+ {"running": true, "degraded": "socket unavailable", "stale_by_version": null, ...}
572
+ (the running version cannot be read without a live socket).
561
573
 
562
574
  R3 (stale socket): if the socket file exists but the server is not
563
575
  responding (ConnectionRefusedError / FileNotFoundError), falls through
@@ -577,12 +589,8 @@ sqlcg mcp stop [OPTIONS]
577
589
 
578
590
  Stop the running MCP server gracefully.
579
591
 
580
- Sends a ``stop`` op via the control socket; waits up to 5 s for the
581
- socket file to disappear (confirming clean exit). Falls back to SIGTERM
582
- on the PID-file PID if the socket is unavailable.
583
-
584
- R3 (stale socket): ``ConnectionRefusedError`` / ``FileNotFoundError`` are
585
- caught — never hangs on a dead socket.
592
+ Delegates to ``stop_server()`` (shared with ``sqlcg install``) and prints
593
+ a final message based on whether a server was found.
586
594
 
587
595
  ### Options
588
596
 
@@ -0,0 +1,390 @@
1
+ # Feature Plan: Version Parity + Auto-Restart of Stale MCP on Reinstall
2
+
3
+ ## Summary
4
+ Make the package version, CLI `--version`, and the running MCP server's reported
5
+ version a single source of truth that is always identical, and make `sqlcg install`
6
+ stop a stale running MCP server so the editor respawns a fresh process. This closes
7
+ the "we debugged against the wrong (stale) MCP server" failure class.
8
+
9
+ ## Motivation / Background
10
+ After a package upgrade the editor kept the old stdio MCP process alive, so debugging
11
+ happened against stale code. There was also no way to ask the *running* MCP server its
12
+ version. This is the follow-up to issue #29 (closed — lifecycle commands) and the
13
+ documented backlog items:
14
+ - ARCHITECTURE_REVIEW.md "minor ui changes": *add version flag to cli `--version`*.
15
+ - ARCHITECTURE_REVIEW.md §11.3 (HIGH): `sqlcg install` leaves stale entries / does not
16
+ refresh the running server.
17
+ - ARCHITECTURE_REVIEW.md §17.4/§17.5: `restart` is `stop` + editor-respawn guidance; a
18
+ true re-parent needs a launcher (out of scope here). We rely on the editor's respawn,
19
+ which is exactly the mechanism this plan leans on.
20
+
21
+ ## Scope
22
+
23
+ ### In Scope
24
+ - An eager `--version` callback on the root [`sqlcg`](src/sqlcg/cli/main.py) Typer app
25
+ (the `version` subcommand stays; both share one helper — no duplicated logic).
26
+ - Set `mcp._mcp_server.version = __version__` in [`server.py`](src/sqlcg/server/server.py)
27
+ so the MCP `initialize` handshake reports `serverInfo.version`.
28
+ - Add a `version` field to the control-socket `status` response and to
29
+ [`mcp status`](src/sqlcg/cli/commands/mcp.py) output, plus a `stale_by_version`
30
+ boolean comparing running version to installed `__version__`.
31
+ - Add a `sqlcg_version` field to the `db_info` MCP tool result so an agent can
32
+ self-check which build it is talking to (the protocol `serverInfo.version` is not
33
+ reliably visible to the agent persona).
34
+ - Make `sqlcg install` stop a running MCP server (best-effort, via the existing control
35
+ socket) on **every** non-dry-run success path — including "already configured" — so the
36
+ editor respawns a fresh process on the new version. The stop fires exactly once, at a
37
+ single shared tail all success branches converge on (see Step 5.1).
38
+ - A test asserting CLI `--version`, MCP `serverInfo.version`, control-socket `status.version`,
39
+ and `db_info.sqlcg_version` all equal `sqlcg.__version__`, and that `__version__`
40
+ equals `importlib.metadata.version("sql-code-graph")`.
41
+ - Version bump (additive surface → minor: `1.4.3` → `1.5.0`), `uv lock`.
42
+
43
+ ### Non-Goals
44
+ - A true re-parenting launcher / true graceful `restart` (deferred per §17.5 — the
45
+ editor owns the stdio process lifecycle; we cannot re-spawn it ourselves).
46
+ - Post-install hooks for `uv tool install` / `pip install` (unreliable across installers
47
+ — explicitly rejected as the trigger; see Design Decision 1).
48
+ - Changing the graph **schema** version concept (`get_schema_version` / `SCHEMA_VERSION`
49
+ in [`tools.py`](src/sqlcg/server/tools.py)). That is a different axis (graph schema, not
50
+ package version) and must not be conflated.
51
+ - Touching any perf-hot path in [`base.py`](src/sqlcg/parsers/base.py) or
52
+ [`indexer.py`](src/sqlcg/indexer/indexer.py).
53
+ - Auto-fixing the §11.3 uvx/sqlcg priority inversion (separate finding; out of scope here).
54
+
55
+ ## Design
56
+
57
+ ### Single source of truth
58
+ Every surface reads `from sqlcg import __version__` (defined at
59
+ [`src/sqlcg/__init__.py:3`](src/sqlcg/__init__.py), kept in lockstep with
60
+ `pyproject.toml` by commitizen `version_files`). No surface hardcodes a version string.
61
+
62
+ Two version axes exist and must stay distinct:
63
+ - **Package version** — `sqlcg.__version__` (this feature). The "are we on the right build?" axis.
64
+ - **Graph schema version** — `get_schema_version()` / `SCHEMA_VERSION` (unchanged). The
65
+ "does the DB match the code's schema?" axis.
66
+
67
+ ### Version surfaces (the four readouts)
68
+ | Surface | How version is exposed | Reads from |
69
+ |---------|------------------------|-----------|
70
+ | CLI flag | `sqlcg --version` eager callback | `sqlcg.__version__` |
71
+ | CLI subcommand (existing) | `sqlcg version` | `sqlcg.__version__` (shared helper) |
72
+ | MCP protocol | `serverInfo.version` in `initialize` handshake | `mcp._mcp_server.version` set to `sqlcg.__version__` |
73
+ | Control socket | `status` JSON `version` field + `mcp status` output | `sqlcg.__version__` in the server process |
74
+ | MCP tool (agent-visible) | `db_info().sqlcg_version` | `sqlcg.__version__` |
75
+
76
+ ### Drift detection
77
+ `mcp status` compares the **running server's** reported `version` (from the live process,
78
+ read over the socket) against the **installed** `sqlcg.__version__` (the version of the
79
+ CLI process you just invoked). If they differ, it emits `stale_by_version: true` and a
80
+ human-readable warning telling the user to restart the MCP server in their editor (or run
81
+ `sqlcg install`, which now stops the stale server for them).
82
+
83
+ ### `mcp._mcp_server.version` — deliberate private access
84
+ `FastMCP.__init__` does **not** accept a `version` kwarg (verified signature); the
85
+ underlying `mcp.server.lowlevel.Server.__init__(self, name, version=None, ...)` does.
86
+ We set it post-construction:
87
+ ```python
88
+ mcp = FastMCP("SQL Code Graph")
89
+ mcp._mcp_server.version = __version__ # deliberate: FastMCP has no version kwarg
90
+ ```
91
+ This is a documented, deliberate private-attribute write. A unit test pins it (so an
92
+ upstream API change that breaks it fails loudly rather than silently shipping
93
+ `serverInfo.version = None` again).
94
+
95
+ ### Data Models
96
+ - [`DbInfoResult`](src/sqlcg/server/models.py) gains:
97
+ ```python
98
+ sqlcg_version: str = Field(..., description="Installed sqlcg package version (sqlcg.__version__)")
99
+ ```
100
+ - Control-socket `status` response dict gains `"version": __version__`.
101
+ - `mcp status` output dict gains `version` (running) and `stale_by_version` (computed).
102
+
103
+ ### Dependencies
104
+ No new runtime dependencies. `importlib.metadata` is stdlib.
105
+
106
+ ## Implementation Steps
107
+
108
+ ### Phase 1: Single source of truth helper
109
+ **Step 1.1**: Add a tiny shared version-string helper.
110
+ - Decision: keep it trivial — both the CLI flag and the `version` subcommand call one
111
+ function so the format string is defined once.
112
+ - File: [`src/sqlcg/cli/main.py`](src/sqlcg/cli/main.py) — add a module-level
113
+ `def _version_string() -> str: from sqlcg import __version__; return f"sqlcg version {__version__}"`.
114
+ - Acceptance: `version` subcommand echoes `_version_string()`; no behaviour change to it.
115
+
116
+ **Step 1.2**: Add an eager `--version` callback on the root app.
117
+ - File: [`src/sqlcg/cli/main.py`](src/sqlcg/cli/main.py).
118
+ - This is the **first `@app.callback()` on the root app** — confirmed none exists today
119
+ (main.py has only `@app.command()` registrations and the `version` subcommand). Use the
120
+ full eager-option-on-a-root-callback pattern below.
121
+ - The callback **must** use `invoke_without_command=True`. Without it, bare `sqlcg --version`
122
+ can error "Missing command" before the eager option's callback gets a chance to run —
123
+ the eager callback fires during parameter processing, but Typer still wants a command
124
+ unless the root callback opts out of requiring one.
125
+ - Exact pattern (state this verbatim in the implementation):
126
+ ```python
127
+ def _version_callback(value: bool) -> None:
128
+ if value:
129
+ typer.echo(_version_string())
130
+ raise typer.Exit()
131
+
132
+ @app.callback(invoke_without_command=True)
133
+ def _root(
134
+ version: bool = typer.Option(
135
+ False, "--version", help="Show version and exit.",
136
+ is_eager=True, callback=_version_callback,
137
+ ),
138
+ ) -> None:
139
+ """SQL code graph analyzer."""
140
+ # No body needed: the eager --version callback exits before we get here.
141
+ # When no subcommand is given and --version is absent, Typer shows help
142
+ # because no_args_is_help is the app default for a group with subcommands.
143
+ ```
144
+ Callback function signature is explicit above: `_root(version: bool = typer.Option(...))`
145
+ returning `None`, and `_version_callback(value: bool) -> None`.
146
+ - Must not break existing subcommand dispatch: the eager callback returns immediately when
147
+ `--version` is not passed, so `_root` runs as a no-op and Typer dispatches the subcommand.
148
+ - Files affected: `main.py`.
149
+ - Acceptance (each is an observable check):
150
+ - (a) `sqlcg --version` prints `sqlcg version 1.5.0` and exits 0.
151
+ - (b) `sqlcg` with no args still shows the help text (not an error).
152
+ - (c) `sqlcg index --help` (and other subcommands, e.g. `sqlcg mcp status`) still dispatch.
153
+ - (d) `sqlcg --help` still renders the root help correctly after the callback is added.
154
+ - (e) `sqlcg version` (the subcommand) still works and echoes `_version_string()`.
155
+
156
+ ### Phase 2: MCP protocol serverInfo.version
157
+ **Step 2.1**: Set `mcp._mcp_server.version` at module scope.
158
+ - File: [`src/sqlcg/server/server.py`](src/sqlcg/server/server.py) — immediately after
159
+ `mcp = FastMCP("SQL Code Graph")` (line 36). Import `__version__` at module top.
160
+ - Add an inline comment documenting why the private attr is used (no `version` kwarg on FastMCP).
161
+ - This must not disturb the fd-1 capture ordering invariant documented at the top of the
162
+ module (the assignment is a pure attribute write, no I/O — safe after line 36).
163
+ - Acceptance: `mcp._mcp_server.version == sqlcg.__version__` (was `None`); the
164
+ `create_initialization_options()` handshake reports `serverInfo.version`.
165
+
166
+ ### Phase 3: Control-socket status version + drift
167
+ **Step 3.1**: Add `version` to the `status` response.
168
+ - File: [`src/sqlcg/server/server.py`](src/sqlcg/server/server.py) — in the `op == "status"`
169
+ branch (the `resp` dict around line 220), add `"version": __version__`.
170
+ - Acceptance: a `{"op":"status"}` request returns a `version` field equal to the running
171
+ server process's `__version__`.
172
+
173
+ **Step 3.2**: Surface running version + drift in `mcp status`.
174
+ - File: [`src/sqlcg/cli/commands/mcp.py`](src/sqlcg/cli/commands/mcp.py) — in `mcp_status`,
175
+ after parsing the framed `status` payload:
176
+ - read `running_version = status.get("version")`.
177
+ - compute `from sqlcg import __version__; stale = running_version is not None and running_version != __version__`.
178
+ - include both in the printed JSON (`version`, `stale_by_version`).
179
+ - if `stale`, print a yellow warning: running vX vs installed vY → restart via editor or run `sqlcg install`.
180
+ - The degraded (PID-only) branch cannot read a version → omit `version`, set
181
+ `stale_by_version` to Python `None` (unknown). Do not guess.
182
+ - **Typing note**: the `mcp status` output is a plain `dict` serialized by
183
+ `console.print_json` / `json.dumps`, NOT a Pydantic model. So the degraded branch simply
184
+ puts Python `None` in the dict, which serializes to JSON `null`. There is no
185
+ `Optional[bool]` Pydantic field to add and no model change for this — it is a dict value.
186
+ - Acceptance: when running and installed versions differ, `mcp status` JSON shows
187
+ `stale_by_version: true` and the warning text; when equal, `stale_by_version: false`
188
+ and no warning.
189
+
190
+ ### Phase 4: Agent-visible version via db_info
191
+ **Step 4.1**: Add `sqlcg_version` to `DbInfoResult`.
192
+ - File: [`src/sqlcg/server/models.py`](src/sqlcg/server/models.py) — add the field (see Data Models).
193
+ - Required field (`...`) — no default — so it cannot silently be omitted.
194
+
195
+ **Step 4.2**: Populate it in the `db_info` tool.
196
+ - File: [`src/sqlcg/server/tools.py`](src/sqlcg/server/tools.py) `db_info` (line 1662) —
197
+ `from sqlcg import __version__` and pass `sqlcg_version=__version__` into the
198
+ `DbInfoResult(...)` return. Keep `schema_version` exactly as-is (do not conflate).
199
+ - Update the `db_info` docstring with one line explaining `sqlcg_version` is the package
200
+ build (distinct from `schema_version`).
201
+ - Acceptance: `db_info()` result carries `sqlcg_version == sqlcg.__version__`; existing
202
+ `schema_version` field unchanged.
203
+
204
+ ### Phase 5: install stops the stale server
205
+ **Step 5.1**: Make `install_cmd` stop a running MCP server on every non-dry-run success path.
206
+
207
+ **5.1a — extract `stop_server()` in `mcp.py` (no install-local wrapper).**
208
+ - File: [`src/sqlcg/cli/commands/mcp.py`](src/sqlcg/cli/commands/mcp.py).
209
+ - Extract the socket-stop body of [`mcp_stop`](src/sqlcg/cli/commands/mcp.py) into a
210
+ reusable module-level function `stop_server() -> bool` in `mcp.py`. It performs the
211
+ existing connect → send `{"op":"stop"}` → `recv(128)` → wait-for-socket-gone → SIGTERM
212
+ fallback logic and returns `True` if a server was stopped, `False` if none was found.
213
+ - `mcp_stop` then calls `stop_server()` and prints its existing messages based on the bool.
214
+ - `install_cmd` imports `stop_server` from `mcp.py` and **calls it directly** — there is
215
+ **no** `_stop_running_server()` install-local wrapper. (The previous draft's
216
+ `_stop_running_server()` wrapper is dead and is removed from this plan.)
217
+ - Grep-confirmed call sites for the new method: `stop_server` is called from `mcp_stop`
218
+ **and** from `install_cmd` — two real call sites, satisfying the "every new method has a
219
+ grep-confirmed call site" rule.
220
+
221
+ **5.1b — single funnel point in `install_cmd` (`install.py`).**
222
+ - File: [`src/sqlcg/cli/commands/install.py`](src/sqlcg/cli/commands/install.py).
223
+ - `install_cmd` today has **FOUR** return/exit shapes; three are non-dry-run success paths
224
+ that must trigger the stop, and dry-run must NOT:
225
+ 1. line 88 — `claude mcp add` succeeds → currently `_provision_skill` + `return`.
226
+ 2. line 115 — "Already configured" (`existing == entry`) → currently `_provision_skill` +
227
+ `return`. **This is exactly the reinstall case**: the config didn't change but the
228
+ binary on disk did, so the stale running server still must be stopped. Do NOT skip it.
229
+ 3. line ~137 — the fallback `~/.claude.json` write path → falls through to
230
+ `_provision_skill` at the function tail.
231
+ 4. dry-run (line 64–74) → `_provision_skill(dry_run=True)` + `return`; must NOT stop,
232
+ prints "would stop running MCP server" instead.
233
+ - **Restructure so all three non-dry-run branches converge on one tail** rather than
234
+ returning early. Concretely: replace the early `return`s at lines 88 and 115 so that,
235
+ after each branch prints its own "Configured" / "Already configured" line, control falls
236
+ through to a single shared tail that runs **once**:
237
+ ```python
238
+ # single non-dry-run tail (reached by all three success branches):
239
+ stopped = stop_server()
240
+ if stopped:
241
+ console.print(
242
+ "Stopped running MCP server; your editor will respawn it on the new build."
243
+ )
244
+ else:
245
+ console.print("No running MCP server to refresh.")
246
+ _provision_skill(resolved_scope, repo, dry_run=False)
247
+ ```
248
+ Use a structure such as: set the per-branch "Configured" message, then `break`/fall into
249
+ the shared tail (e.g. wrap the three branches so they assign and then jump past to the
250
+ tail, or invert the early-returns into `if/elif/else` so the tail is unconditionally
251
+ reached on the non-dry-run path). The dry-run block keeps its own early `return` and
252
+ prints "would stop running MCP server" before returning.
253
+ - `stop_server()` is called **exactly once** on the non-dry-run path — do NOT duplicate
254
+ the call in all three branches, and do NOT miss the "Already configured" branch.
255
+ - The stop is best-effort: `stop_server()` returning `False` (no server) is a no-op with
256
+ the "No running MCP server to refresh." message; a socket error inside `stop_server()` is
257
+ already swallowed by its own except clause and must not abort install.
258
+
259
+ **Message (resolves the `(vX)` inconsistency):** the install stop line **drops the `(vX)`
260
+ version suffix**. `stop_server() -> bool` returns only a bool, not the stopped server's
261
+ version, and querying `status` first just to render `(vX)` would add an extra socket
262
+ round-trip for no real benefit. So the message is exactly:
263
+ "Stopped running MCP server; your editor will respawn it on the new build."
264
+
265
+ - Acceptance: with a running server, `sqlcg install` (non-dry-run) sends a `stop` op and
266
+ the server exits, for **all three** success branches including "Already configured";
267
+ `stop_server()` is invoked exactly once per install; with no server, install completes
268
+ with the no-op message; `--dry-run` never stops anything and prints "would stop running
269
+ MCP server".
270
+
271
+ ### Phase 6: Single-source-of-truth test
272
+ **Step 6.1**: Add a parity test.
273
+ - File: `tests/unit/test_version_parity.py`.
274
+ - Assert:
275
+ 1. `sqlcg.__version__ == importlib.metadata.version("sql-code-graph")`.
276
+ 2. CLI `--version` output (via Typer `CliRunner`) contains `sqlcg.__version__`.
277
+ 3. `mcp._mcp_server.version == sqlcg.__version__` (import
278
+ [`server.py`](src/sqlcg/server/server.py); pins the private-attr set survives upstream changes).
279
+ 4. `db_info().sqlcg_version == sqlcg.__version__` (integration-level, with a backend) —
280
+ OR a focused unit asserting `DbInfoResult` carries the field and the tool passes
281
+ `__version__` (use whichever matches existing `db_info` test infra; prefer the
282
+ integration path if one already exists).
283
+ 5. Control-socket `status` includes `version == sqlcg.__version__`. Home this assertion
284
+ in [`tests/unit/test_mcp_control.py`](tests/unit/test_mcp_control.py) (the existing
285
+ control-socket test module). The assertion must inspect **observable output**, NOT
286
+ source-grep the `resp` dict construction. Only these two forms are acceptable:
287
+ (a) drive the real control socket end-to-end and parse the framed `status` response,
288
+ asserting the parsed `version` equals `sqlcg.__version__`; or
289
+ (b) call the `status` handler directly and inspect the returned dict's `version` value.
290
+ Do not assert by reading the server source for the literal `"version": __version__`.
291
+ - Acceptance: all five assertions pass; the test fails if any surface diverges or
292
+ hardcodes a version.
293
+
294
+ ### Phase 7: Version bump + lock
295
+ **Step 7.1**: Bump version to `1.5.0`.
296
+ - [`pyproject.toml`](pyproject.toml) `version = "1.5.0"`.
297
+ - [`src/sqlcg/__init__.py`](src/sqlcg/__init__.py) `__version__ = "1.5.0"`.
298
+ - Run `uv lock`.
299
+ - Acceptance: `uv run sqlcg --version` prints `sqlcg version 1.5.0`; the parity test passes
300
+ against `1.5.0`; `git diff` shows the lockfile's own version entry refreshed.
301
+ - NOTE: the agent does NOT tag or close issues — the user tags after merge (per CLAUDE.md
302
+ "Releasing" + project memory "never close; verify before tag").
303
+
304
+ ## Design Decisions (resolved)
305
+
306
+ ### Decision 1 — Restart-on-reinstall trigger: `sqlcg install` stops the running server (option a), plus a drift *warning* in `mcp status` (option b as detection, not auto-stop)
307
+ **Recommendation: a + b (detection only).**
308
+ - uv-tool/pip installs do not reliably run post-install hooks, so an installer hook is
309
+ rejected. The trigger that the user already performs after an upgrade is `sqlcg install`
310
+ (it is in the QUICK START). Making `install` stop the stale server (Phase 5) is the
311
+ reliable, explicit trigger: stop → editor respawns the freshly-installed build.
312
+ - `mcp status` (Phase 3) is a *detector*, not an auto-killer. Auto-stopping a server from a
313
+ read-only `status` call would be a surprising side effect. It instead reports
314
+ `stale_by_version` and tells the user the fix. This respects "no TODO in happy path"
315
+ (the stop path is fully implemented in install; status just reports).
316
+ - We rely on the editor respawning the stdio process — consistent with §17.4/§17.5 (we
317
+ cannot re-parent it ourselves; the editor owns it). This is honest, not faked.
318
+
319
+ ### Decision 2 — `--version` for the MCP: implement all three readouts
320
+ **Recommendation: implement the protocol field AND the operator/agent readouts.**
321
+ - (i) Protocol `serverInfo.version` (Phase 2) is the canonical answer to "what version is
322
+ the running server?" but is not reliably visible to the agent persona or to a human at
323
+ the CLI.
324
+ - (ii) `mcp status` `version` field (Phase 3) is the human/operator readout and the place
325
+ drift is flagged.
326
+ - (iii) `db_info().sqlcg_version` (Phase 4) is the agent-callable readout so an agent can
327
+ self-check it is on the right build before trusting results.
328
+ All three are needed to actually let someone confirm which version is running.
329
+
330
+ ### Decision 3 — Single source of truth + parity test
331
+ **Recommendation: every surface reads `from sqlcg import __version__`; one test pins parity.**
332
+ - No surface hardcodes a version. Phase 6 asserts the four runtime surfaces agree with
333
+ `__version__` and that `__version__` equals the installed distribution metadata
334
+ (`importlib.metadata.version("sql-code-graph")`) — verified resolvable in this env
335
+ (returns `1.4.3` today). This catches a future `pyproject.toml`/`__init__.py` drift even
336
+ if commitizen is bypassed.
337
+
338
+ ## Test Strategy
339
+ - **Unit**:
340
+ - `tests/unit/test_version_parity.py` (Phase 6) — the five parity assertions.
341
+ - CLI `--version` via Typer `CliRunner` — observable stdout asserted (not "no exception").
342
+ - `mcp status` drift: mock the socket `status` payload with a mismatched `version`,
343
+ assert `stale_by_version: true` + warning text in output; matching version → false + no warning.
344
+ - `install` stop-on-reinstall: mock `stop_server()`, assert it is called **exactly once**
345
+ on the non-dry-run path — including the "Already configured" branch — and NOT called on
346
+ `--dry-run`; assert the no-op message when it returns False; assert the stop message has
347
+ no `(vX)` suffix.
348
+ - Private-attr guard: `mcp._mcp_server.version == __version__` (fails loudly if FastMCP
349
+ upstream changes).
350
+ - **Integration** (real backend, in-memory): `db_info().sqlcg_version == __version__`
351
+ alongside an existing `db_info` integration test if one exists; otherwise add a focused one.
352
+ - **Observable-output rule**: every test asserts a concrete value (version string, boolean,
353
+ warning substring), never merely that a call did not raise.
354
+ - Run full gate before handoff: `uv run pytest`, `uv run pyright`, `uv run ruff check src tests`.
355
+
356
+ ## Acceptance Criteria
357
+ - [ ] `sqlcg --version` prints `sqlcg version <__version__>` and exits 0; `sqlcg` with no
358
+ args still shows help; `sqlcg --help` renders correctly after the callback is added;
359
+ `sqlcg version` still works; all other subcommands (e.g. `sqlcg index --help`,
360
+ `sqlcg mcp status`) dispatch unchanged.
361
+ - [ ] `mcp._mcp_server.version == sqlcg.__version__` (no longer `None`); the MCP
362
+ `initialize` handshake reports `serverInfo.version`.
363
+ - [ ] Control-socket `status` response includes `version == <running __version__>`.
364
+ - [ ] `sqlcg mcp status` shows `version` and `stale_by_version`, and warns when the running
365
+ server version differs from the installed `__version__`.
366
+ - [ ] `db_info().sqlcg_version == sqlcg.__version__`; `schema_version` unchanged and distinct.
367
+ - [ ] `sqlcg install` (non-dry-run) stops a running MCP server so the editor respawns it,
368
+ on all three success branches including "Already configured"; `stop_server()` is
369
+ invoked exactly once per install; no-op message when none running; `--dry-run` stops
370
+ nothing. Stop message drops the `(vX)` suffix.
371
+ - [ ] `stop_server()` lives in `mcp.py` (no install-local wrapper) and has grep-confirmed
372
+ call sites in both `mcp_stop` and `install_cmd`.
373
+ - [ ] Parity test asserts all four runtime surfaces equal `__version__` and
374
+ `__version__ == importlib.metadata.version("sql-code-graph")`.
375
+ - [ ] Version bumped to `1.5.0` in `pyproject.toml` + `__init__.py`; `uv lock` run.
376
+ - [ ] No TODO in any happy path; no changes to `base.py`/`indexer.py`.
377
+
378
+ ## Risks and Mitigations
379
+ | Risk | Mitigation |
380
+ |------|-----------|
381
+ | FastMCP upstream removes/renames `_mcp_server` (private attr) | Unit test pins `mcp._mcp_server.version == __version__`; a break fails the suite loudly instead of silently shipping `None`. |
382
+ | `install` stopping a server the user did not want stopped | Stop is best-effort + clearly messaged; install's whole purpose post-upgrade is to refresh the server; `--dry-run` never stops. |
383
+ | `importlib.metadata.version("sql-code-graph")` differs in an editable/uv dev tree | Verified it returns the correct value in this env. If a future dev layout breaks it, the parity test surfaces it immediately (intended). |
384
+ | Conflating package version with graph schema version | Distinct fields (`sqlcg_version` vs `schema_version`), distinct docstrings, called out in Non-Goals. |
385
+ | Editor does not auto-respawn after stop | Documented limitation (§17.4/§17.5); `mcp status` warning + install message tell the user to restart via the editor. |
386
+
387
+ ## Rollout / Rollback
388
+ - Additive surface only; no schema change → no re-index required. Rollback = revert the PR.
389
+ - Ships as one feature PR that bumps `1.4.3 → 1.5.0` (SemVer minor: additive, nothing
390
+ breaks). User tags `v1.5.0` on the master merge commit after merge (agent does not tag).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sql-code-graph"
7
- version = "1.4.3"
7
+ version = "1.5.1"
8
8
  description = "SQL code graph analyzer and lineage tracer"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -1,5 +1,5 @@
1
1
  """SQL Code Graph - SQL lineage and dependency analysis tool."""
2
2
 
3
- __version__ = "1.4.3"
3
+ __version__ = "1.5.1"
4
4
 
5
5
  __all__ = ["__version__"]