sql-code-graph 1.1.3__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (437) hide show
  1. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/ARCHITECTURE_REVIEW.md +329 -1
  2. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/CLAUDE.md +21 -0
  3. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/PKG-INFO +11 -1
  4. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/README.md +10 -0
  5. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/docs/cli.md +111 -8
  6. sql_code_graph-1.3.0/plan/fix_downstream_sink_location.md +119 -0
  7. sql_code_graph-1.3.0/plan/fix_issue29_live_test_followups.md +442 -0
  8. sql_code_graph-1.3.0/plan/v1_2_0_read_proxy.md +597 -0
  9. sql_code_graph-1.3.0/plan/v1_2_1_bugfix.md +669 -0
  10. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/pyproject.toml +1 -1
  11. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/__init__.py +1 -1
  12. sql_code_graph-1.3.0/src/sqlcg/cli/commands/analyze.py +327 -0
  13. sql_code_graph-1.3.0/src/sqlcg/cli/commands/db.py +210 -0
  14. sql_code_graph-1.3.0/src/sqlcg/cli/commands/find.py +78 -0
  15. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/gain.py +13 -11
  16. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/git.py +11 -4
  17. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/index.py +167 -4
  18. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/mcp.py +70 -3
  19. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/reindex.py +146 -76
  20. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/config.py +15 -13
  21. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/kuzu_backend.py +10 -6
  22. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/indexer.py +91 -11
  23. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/lineage/aggregator.py +17 -45
  24. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/metrics/store.py +48 -0
  25. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/ansi_parser.py +2 -2
  26. sql_code_graph-1.3.0/src/sqlcg/server/read_client.py +192 -0
  27. sql_code_graph-1.3.0/src/sqlcg/server/server.py +556 -0
  28. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/tools.py +155 -14
  29. sql_code_graph-1.3.0/src/sqlcg/server/writer.py +634 -0
  30. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_mcp_lifecycle.py +29 -9
  31. sql_code_graph-1.3.0/tests/integration/test_cross_file_lineage.py +125 -0
  32. sql_code_graph-1.3.0/tests/integration/test_dialect_auto_resolution.py +178 -0
  33. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_indexer_commits.py +7 -2
  34. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_pr3_kind_tagging.py +24 -19
  35. sql_code_graph-1.3.0/tests/integration/test_read_via_server.py +235 -0
  36. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_readonly_under_lock.py +31 -22
  37. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_reindex_via_server.py +60 -28
  38. sql_code_graph-1.3.0/tests/integration/test_single_writer_queue.py +382 -0
  39. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_star_resolution.py +10 -2
  40. sql_code_graph-1.3.0/tests/integration/test_user_surface_recall_guard.py +455 -0
  41. sql_code_graph-1.3.0/tests/unit/test_BugB_escalation_uses_init_path.py +101 -0
  42. sql_code_graph-1.3.0/tests/unit/test_BugC_hook_upgrade.py +184 -0
  43. sql_code_graph-1.3.0/tests/unit/test_aggregator.py +68 -0
  44. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_aggregator_skip.py +21 -30
  45. sql_code_graph-1.3.0/tests/unit/test_canonical_target_resolution.py +270 -0
  46. sql_code_graph-1.3.0/tests/unit/test_db_info.py +142 -0
  47. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_find_cmd.py +19 -28
  48. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_freshness_helper.py +20 -21
  49. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_gain_ratio.py +5 -12
  50. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_git_hooks_notify.py +29 -0
  51. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_hygiene_config_warning.py +2 -4
  52. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_kuzu_lock.py +4 -4
  53. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_noise_filter.py +8 -2
  54. sql_code_graph-1.3.0/tests/unit/test_read_client.py +234 -0
  55. sql_code_graph-1.3.0/tests/unit/test_resolve_pass2_passes_dependency_filter.py +174 -0
  56. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_star_schema_unit.py +26 -25
  57. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_unqualified_fallback.py +9 -24
  58. sql_code_graph-1.3.0/tests/unit/test_writer_queue.py +323 -0
  59. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/uv.lock +1 -1
  60. sql_code_graph-1.1.3/src/sqlcg/cli/commands/analyze.py +0 -325
  61. sql_code_graph-1.1.3/src/sqlcg/cli/commands/db.py +0 -181
  62. sql_code_graph-1.1.3/src/sqlcg/cli/commands/find.py +0 -81
  63. sql_code_graph-1.1.3/src/sqlcg/server/server.py +0 -382
  64. sql_code_graph-1.1.3/tests/integration/test_cross_file_lineage.py +0 -129
  65. sql_code_graph-1.1.3/tests/unit/test_aggregator.py +0 -55
  66. sql_code_graph-1.1.3/tests/unit/test_db_info.py +0 -173
  67. sql_code_graph-1.1.3/tests/unit/test_resolve_pass2_passes_dependency_filter.py +0 -149
  68. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/api-documenter.md +0 -0
  69. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/architect-planner.md +0 -0
  70. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/architect-reviewer.md +0 -0
  71. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/code-reviewer.md +0 -0
  72. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/developer.md +0 -0
  73. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/plan-reviewer.md +0 -0
  74. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.claude/agents/sprint-planner.md +0 -0
  75. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  76. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  77. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  78. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/workflows/benchmark.yml +0 -0
  79. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/workflows/e2e-tests.yml +0 -0
  80. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/workflows/release.yml +0 -0
  81. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.github/workflows/test.yml +0 -0
  82. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.gitignore +0 -0
  83. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.pre-commit-config.yaml +0 -0
  84. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/.sqlcgignore +0 -0
  85. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/CHANGELOG.md +0 -0
  86. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/docs/AIRBNB_PARSE_REPORT.md +0 -0
  87. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/e2e_firstuser_report.md +0 -0
  88. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/e2e_run_20260528_101413.output +0 -0
  89. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/main.py +0 -0
  90. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/WORKFLOW.md +0 -0
  91. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/blueprint.md +0 -0
  92. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/bundle_claude_skill.md +0 -0
  93. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/feature_34_unused_presentation_segregation.md +0 -0
  94. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/feature_35_external_downstream_injection.md +0 -0
  95. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/feature_F2_bundle_claude_skill.md +0 -0
  96. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/fix_dynamic_table_parsing.md +0 -0
  97. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/fix_expand_qualify_perf.md +0 -0
  98. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/fix_firstuser_findings.md +0 -0
  99. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/fix_schema_case_mismatch.md +0 -0
  100. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/hygiene_config_path_and_survivors.md +0 -0
  101. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/investigation_e5_e4.md +0 -0
  102. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/living_codebase_resync.md +0 -0
  103. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/measurements/schema_comparison_with_schema.json +0 -0
  104. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/measurements/schema_comparison_without_schema.json +0 -0
  105. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/measurements/sprint_08_changelogs_fullindex.json +0 -0
  106. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/measurements/sprint_08_fullcorpus_index.json +0 -0
  107. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/measurements/sprint_pool_300s_plan.json +0 -0
  108. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/parse_diagnostics.md +0 -0
  109. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/parsing_errors_experiment.md +0 -0
  110. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/snowflake_en_test_suite.md +0 -0
  111. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_01.md +0 -0
  112. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_01_deployment_pypi.md +0 -0
  113. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_02.md +0 -0
  114. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_02_v0.3.0_core.md +0 -0
  115. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_03.md +0 -0
  116. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_03_v0.3.1_postmortem.md +0 -0
  117. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_04_column_lineage.md +0 -0
  118. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_04_column_lineage_fix.md +0 -0
  119. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_05_star_resolution.md +0 -0
  120. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_06_lineage_coverage.md +0 -0
  121. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_07_open_ecodes.md +0 -0
  122. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_07_perf_and_live_test.md +0 -0
  123. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_08_perf_upsert.md +0 -0
  124. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_09_lineage_coverage.md +0 -0
  125. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_10_anchor_tools.md +0 -0
  126. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_11_v1.0.2_bugfix.md +0 -0
  127. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_12_v1.1.0.md +0 -0
  128. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_13_v1.1.0_cluster_b.md +0 -0
  129. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sprint_3.1_postmortem.md +0 -0
  130. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/sqlcg.md +0 -0
  131. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/trust_layer.md +0 -0
  132. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/v1.1.0_cluster_b_provenance_trust.md +0 -0
  133. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/v1.1.0_live_graph_freshness.md +0 -0
  134. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/v1.1.1_batch_upsert_perf.md +0 -0
  135. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/v1_1_2_bugfix.md +0 -0
  136. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/plan/v1_1_3_union_cte_star.md +0 -0
  137. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/profile.html +0 -0
  138. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/pyrightconfig.json +0 -0
  139. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/scripts/collect_parse_errors.py +0 -0
  140. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/scripts/generate_cli_docs.sh +0 -0
  141. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/__main__.py +0 -0
  142. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/__init__.py +0 -0
  143. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/__init__.py +0 -0
  144. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/install.py +0 -0
  145. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/report.py +0 -0
  146. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/uninstall.py +0 -0
  147. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/commands/watch.py +0 -0
  148. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/cli/main.py +0 -0
  149. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/__init__.py +0 -0
  150. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/freshness.py +0 -0
  151. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/graph_db.py +0 -0
  152. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/jobs.py +0 -0
  153. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/neo4j_backend.py +0 -0
  154. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/queries.cypher +0 -0
  155. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/queries.py +0 -0
  156. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/schema.cypher +0 -0
  157. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/core/schema.py +0 -0
  158. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/__init__.py +0 -0
  159. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/dbt_adapter.py +0 -0
  160. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/error_classify.py +0 -0
  161. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/git_delta.py +0 -0
  162. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/pool.py +0 -0
  163. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/walker.py +0 -0
  164. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/indexer/watcher.py +0 -0
  165. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/lineage/__init__.py +0 -0
  166. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/lineage/schema_resolver.py +0 -0
  167. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/metrics/__init__.py +0 -0
  168. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/__init__.py +0 -0
  169. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/base.py +0 -0
  170. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/bigquery_parser.py +0 -0
  171. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/postgres_parser.py +0 -0
  172. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/registry.py +0 -0
  173. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/snowflake_parser.py +0 -0
  174. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/parsers/tsql_parser.py +0 -0
  175. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/__init__.py +0 -0
  176. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/control.py +0 -0
  177. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/exceptions.py +0 -0
  178. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/models.py +0 -0
  179. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/noise_filter.py +0 -0
  180. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/server/skill.py +0 -0
  181. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/utils/__init__.py +0 -0
  182. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/utils/hashing.py +0 -0
  183. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/utils/ignore.py +0 -0
  184. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/src/sqlcg/utils/logging.py +0 -0
  185. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/__init__.py +0 -0
  186. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/__init__.py +0 -0
  187. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/adversarial/200_join.sql +0 -0
  188. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/adversarial/500_union.sql +0 -0
  189. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/bench_indexer.py +0 -0
  190. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/conftest.py +0 -0
  191. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/case_normalization.sql +0 -0
  192. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/colon_cast.sql +0 -0
  193. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/colon_reserved_word.sql +0 -0
  194. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/copy_into.sql +0 -0
  195. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/create_procedure.sql +0 -0
  196. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/identifier_dynamic.sql +0 -0
  197. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/lateral_flatten.sql +0 -0
  198. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/qualify.sql +0 -0
  199. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/scripting_block.sql +0 -0
  200. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/golden_corpus/snowflake/three_part.sql +0 -0
  201. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/tpch/q01.sql +0 -0
  202. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/tpch/q02.sql +0 -0
  203. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/tpch/q03.sql +0 -0
  204. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/tpch/q04.sql +0 -0
  205. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/benchmarks/tpch/q05.sql +0 -0
  206. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/__init__.py +0 -0
  207. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/conftest.py +0 -0
  208. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_F2_skill_install_e2e.py +0 -0
  209. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_airbnb_e2e.py +0 -0
  210. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_cli_index.py +0 -0
  211. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_git_hook_install.py +0 -0
  212. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_golden_lineage.py +0 -0
  213. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_mcp_tools.py +0 -0
  214. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_parse_diagnostics_cli.py +0 -0
  215. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_star_resolution_e2e.py +0 -0
  216. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/e2e/test_watch.py +0 -0
  217. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/dim_hosts_cleansed.sql +0 -0
  218. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/dim_listings_cleansed.sql +0 -0
  219. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/fct_reviews.sql +0 -0
  220. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/mart_fullmoon_reviews.sql +0 -0
  221. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/raw_hosts.sql +0 -0
  222. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/raw_listings.sql +0 -0
  223. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/raw_reviews.sql +0 -0
  224. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/src_hosts.sql +0 -0
  225. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/src_listings.sql +0 -0
  226. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/airbnb/src_reviews.sql +0 -0
  227. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/bigquery/.gitkeep +0 -0
  228. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/jaffle_shop/customers.sql +0 -0
  229. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/jaffle_shop/orders.sql +0 -0
  230. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/jaffle_shop/raw_orders.sql +0 -0
  231. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/snowflake/base_tables.sql +0 -0
  232. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/snowflake/reports.sql +0 -0
  233. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/snowflake/views.sql +0 -0
  234. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/star_corpus/ddl_src.sql +0 -0
  235. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/star_corpus/ddl_tgt.sql +0 -0
  236. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/star_corpus/etl_alias_star.sql +0 -0
  237. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/star_corpus/etl_star.sql +0 -0
  238. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/synthetic/base_tables.sql +0 -0
  239. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/synthetic/reports.sql +0 -0
  240. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/fixtures/synthetic/views.sql +0 -0
  241. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/__init__.py +0 -0
  242. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/snowflake/__init__.py +0 -0
  243. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/snowflake/test_insert_select.py +0 -0
  244. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_T34_presentation_segregation.py +0 -0
  245. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_T35_external_consumers.py +0 -0
  246. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_anchor_tools.py +0 -0
  247. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_bulk_upsert.py +0 -0
  248. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_column_lineage_e2e.py +0 -0
  249. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_cte_recall_guard.py +0 -0
  250. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_cte_schema_alias_guard.py +0 -0
  251. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_cte_source_node_invariant.py +0 -0
  252. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_dialect_matrix.py +0 -0
  253. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_e36_xfile_regression_guard.py +0 -0
  254. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_freshness_mcp.py +0 -0
  255. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_hygiene_config_root_reconciliation.py +0 -0
  256. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_indexer_batching.py +0 -0
  257. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_indexer_to_graph.py +0 -0
  258. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_live_anchors.py +0 -0
  259. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_parse_diagnostics.py +0 -0
  260. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_pr1_confidence_reason.py +0 -0
  261. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_pr2_source_location.py +0 -0
  262. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_resync.py +0 -0
  263. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_temp_table_lineage.py +0 -0
  264. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/integration/test_union_cte_star_recall_guard.py +0 -0
  265. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/perf/__init__.py +0 -0
  266. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/perf/test_perf.py +0 -0
  267. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E10/__init__.py +0 -0
  268. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E11/__init__.py +0 -0
  269. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E12/__init__.py +0 -0
  270. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E12/e12_json_path.sql +0 -0
  271. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E12/e12_lateral_flatten.sql +0 -0
  272. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E12/test_e12.py +0 -0
  273. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E13/__init__.py +0 -0
  274. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E14/__init__.py +0 -0
  275. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E15/__init__.py +0 -0
  276. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E16/__init__.py +0 -0
  277. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E16/e16_merge.sql +0 -0
  278. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E16/e16_merge_delete.sql +0 -0
  279. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E16/test_e16.py +0 -0
  280. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E17/__init__.py +0 -0
  281. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E18/__init__.py +0 -0
  282. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E18/e18_decode.sql +0 -0
  283. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E18/e18_iff_decode.sql +0 -0
  284. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E18/e18_nvl2.sql +0 -0
  285. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E18/test_e18.py +0 -0
  286. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E19/__init__.py +0 -0
  287. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E2/__init__.py +0 -0
  288. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E2/e2_expr_alias.sql +0 -0
  289. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E2/e2_function_alias.sql +0 -0
  290. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E2/e2_multiply_alias.sql +0 -0
  291. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E2/test_e2.py +0 -0
  292. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E20/__init__.py +0 -0
  293. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E21/__init__.py +0 -0
  294. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E21/e21_alias_forward_ref.sql +0 -0
  295. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E21/e21_three_level_chain.sql +0 -0
  296. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E21/test_e21.py +0 -0
  297. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E22/__init__.py +0 -0
  298. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E23/__init__.py +0 -0
  299. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E23/e23_stored_proc.sql +0 -0
  300. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E23/e23_stored_proc_multi.sql +0 -0
  301. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E23/test_e23.py +0 -0
  302. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E24/__init__.py +0 -0
  303. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E25/__init__.py +0 -0
  304. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E25/e25_cross_db.sql +0 -0
  305. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E25/e25_two_part.sql +0 -0
  306. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E25/test_e25.py +0 -0
  307. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E25/test_e25_full_id.py +0 -0
  308. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E26/__init__.py +0 -0
  309. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E27/__init__.py +0 -0
  310. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E27/e27_nested_udf.sql +0 -0
  311. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E27/e27_udf.sql +0 -0
  312. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E27/test_e27.py +0 -0
  313. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E28/__init__.py +0 -0
  314. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E29/__init__.py +0 -0
  315. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E3/__init__.py +0 -0
  316. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E3/e3_alter_table.sql +0 -0
  317. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E3/e3_create_sequence.sql +0 -0
  318. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E3/e3_ddl_only.sql +0 -0
  319. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E3/test_e3.py +0 -0
  320. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E30/__init__.py +0 -0
  321. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E31/__init__.py +0 -0
  322. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E32/__init__.py +0 -0
  323. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E33/__init__.py +0 -0
  324. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E34/__init__.py +0 -0
  325. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E35/__init__.py +0 -0
  326. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E36/__init__.py +0 -0
  327. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E36/e36_temp_multi_use.sql +0 -0
  328. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E36/e36_temp_table.sql +0 -0
  329. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E36/test_e36.py +0 -0
  330. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E36/test_e36_xfile.py +0 -0
  331. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E37/__init__.py +0 -0
  332. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E38/__init__.py +0 -0
  333. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E4/__init__.py +0 -0
  334. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E4/e4_execute_immediate.sql +0 -0
  335. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E4/e4_if_not_exists.sql +0 -0
  336. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E4/e4_unexpected_token.sql +0 -0
  337. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E4/e4_unpivot.sql +0 -0
  338. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E4/test_e4.py +0 -0
  339. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E5/__init__.py +0 -0
  340. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E5/e5_cte_missing_source.sql +0 -0
  341. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E5/e5_multi_cte.sql +0 -0
  342. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E5/e5_nested_cte.sql +0 -0
  343. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E5/test_e5.py +0 -0
  344. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E5/test_e5_cte_projection.py +0 -0
  345. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E8/__init__.py +0 -0
  346. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E8/e8_dynamic_sources.sql +0 -0
  347. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E8/e8_seq_nextval.sql +0 -0
  348. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E8/e8_uuid.sql +0 -0
  349. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E8/test_e8.py +0 -0
  350. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E9/__init__.py +0 -0
  351. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_aggregates/__init__.py +0 -0
  352. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_aggregates/fixture_sum_absent_cross_schema.sql +0 -0
  353. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_aggregates/fixture_sum_case_when.sql +0 -0
  354. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_aggregates/fixture_sum_present_source.sql +0 -0
  355. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_aggregates/test_e_aggregates.py +0 -0
  356. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_date_functions/__init__.py +0 -0
  357. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_date_functions/fixture_date_aliased.sql +0 -0
  358. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_date_functions/fixture_date_unaliased.sql +0 -0
  359. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_date_functions/fixture_datediff_unaliased.sql +0 -0
  360. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_date_functions/fixture_year_unaliased.sql +0 -0
  361. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/E_date_functions/test_e_date_functions.py +0 -0
  362. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/README.md +0 -0
  363. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/__init__.py +0 -0
  364. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/__init__.py +0 -0
  365. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/fixture_etl.sql +0 -0
  366. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/fixture_omloopsnelheid.sql +0 -0
  367. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/fixture_semantic.sql +0 -0
  368. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/fixture_source.sql +0 -0
  369. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/test_anchor_ma_aantal_op_order.py +0 -0
  370. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/anchors/test_anchor_omloopsnelheid.py +0 -0
  371. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/conftest.py +0 -0
  372. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/snowflake/test_plan_review_gates.py +0 -0
  373. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/__init__.py +0 -0
  374. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/snowflake/__init__.py +0 -0
  375. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/snowflake/test_scripting_noise.py +0 -0
  376. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_F2_install_skill.py +0 -0
  377. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_F2_skill_render.py +0 -0
  378. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_F2_uninstall_skill.py +0 -0
  379. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_T09_01_qualify_once.py +0 -0
  380. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_T09_02_ddl_skip.py +0 -0
  381. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_T09_04_subprocess_isolate.py +0 -0
  382. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_T09_06_log_verbosity.py +0 -0
  383. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_T35_config_external_consumers.py +0 -0
  384. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_base_parser.py +0 -0
  385. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_branch_monitor.py +0 -0
  386. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_bulk_upsert_invariant.py +0 -0
  387. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_cli.py +0 -0
  388. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_cli_help.py +0 -0
  389. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_closure_depth.py +0 -0
  390. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_column_lineage_wiring.py +0 -0
  391. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_config.py +0 -0
  392. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_data_models.py +0 -0
  393. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_dominant_cause.py +0 -0
  394. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_firstuser_findings.py +0 -0
  395. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_git_delta.py +0 -0
  396. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_git_hooks.py +0 -0
  397. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_graph_backend.py +0 -0
  398. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_hard_kill_pool.py +0 -0
  399. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_include_working_tree.py +0 -0
  400. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_index_cmd.py +0 -0
  401. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_index_flags.py +0 -0
  402. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_index_progress.py +0 -0
  403. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_indexer_progress.py +0 -0
  404. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_indexer_quality.py +0 -0
  405. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_install.py +0 -0
  406. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_install_message.py +0 -0
  407. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_jobs.py +0 -0
  408. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_judgement.py +0 -0
  409. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_kuzu_backend.py +0 -0
  410. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_lineage_conversion.py +0 -0
  411. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_literal_column_skip.py +0 -0
  412. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_mcp_best_practices.py +0 -0
  413. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_mcp_control.py +0 -0
  414. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_mcp_stdio_smoke.py +0 -0
  415. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_metrics.py +0 -0
  416. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_parse_file_dependency_filter.py +0 -0
  417. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_parse_quality.py +0 -0
  418. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_parser.py +0 -0
  419. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_perf_scaling_guard.py +0 -0
  420. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_pr07_observability.py +0 -0
  421. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_queries_loader.py +0 -0
  422. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_schema_resolver.py +0 -0
  423. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_server.py +0 -0
  424. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_snowflake_strip_alter_set_tag.py +0 -0
  425. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_sprint_06_t04_t05.py +0 -0
  426. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_star_resolution_unit.py +0 -0
  427. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_submit_feedback.py +0 -0
  428. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_subprocess_isolate.py +0 -0
  429. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_t02_expression_name_extraction.py +0 -0
  430. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_t03_ddl_skip.py +0 -0
  431. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_timeout_cancel.py +0 -0
  432. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_tools_hints.py +0 -0
  433. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_tools_warnings.py +0 -0
  434. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_uninstall.py +0 -0
  435. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_upsert_batch_invariant.py +0 -0
  436. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/tests/unit/test_walker.py +0 -0
  437. {sql_code_graph-1.1.3 → sql_code_graph-1.3.0}/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,324 @@ 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** — **IMPLEMENTED in 1.3.0**
3030
+ (#29 single-writer queue + RO→RW escalation; plan
3031
+ [`fix_issue_29_single_writer_queue.md`](plan/fix_issue_29_single_writer_queue.md)).
3032
+ The read half was already handled by the v1.2.0 routing proxy; 1.3.0 adds the write half:
3033
+ the server serves RO after a startup RW schema-ensure window, and escalates RO→RW only for
3034
+ the duration of a single-writer drain. See §20 for the compliance postmortem and two
3035
+ accepted/flagged deviations.
2950
3036
  - **`connected_clients` fidelity** — best-effort `1` today; no real counter.
3037
+
3038
+ ## 19. v1.2.1 live DWH re-verification — two residual findings (2026-06-02)
3039
+
3040
+ Source: live verification of the v1.2.1 fixes against the full DWH corpus
3041
+ (`../dwh`, 1,335 SQL files, clean index ~1:56). **The v1.2.1 fixes themselves
3042
+ PASSED** — both findings below are residual / design-limit behaviour surfaced by
3043
+ the run, not regressions in PR #48. Corpus graph snapshot (kind-typed `SqlTable`):
3044
+ 2,222 `table` / 383 `cte` / 272 `derived`.
3045
+
3046
+ ### 19.1 [KEEP — LOW] Residual scratch-object leak into the default `analyze` surface
3047
+
3048
+ **Measurement.** The #45.2 fix essentially closed the `cte_` leak it targeted:
3049
+ of nodes still typed `kind='table'`, only 3 carry a `cte_` prefix (vs 383 now
3050
+ correctly `kind='cte'`). The broader leak persists: **495 of 2,222 `table`-kind
3051
+ nodes (~22%) carry scratch-object name prefixes** — 471 `tmp_*`, 21 `temp_*`,
3052
+ 3 `cte_*` — plus ~575 unqualified single-token nodes (a mix of real bare tables
3053
+ and CTE aliases). These surface in default (filtered) `analyze` output because the
3054
+ filter is correctly kind-based (graph-truth) but these aliases were never classified
3055
+ `cte`/`derived` at parse/aggregation time. This is the known #38 islands residual
3056
+ (s18.4 / `47 cte_insert islands`); the leak is now `tmp_*`-dominated, not
3057
+ `cte_*`-dominated. See memory `project_issue38_cte_filter_regression`.
3058
+
3059
+ **Decision: KEEP as a future ticket, LOW priority.** Rationale tied to existing
3060
+ decisions:
3061
+ - The standing **"no name-prefix filtering"** decision (s18.2; CLAUDE.md) is correct
3062
+ and stays. The fix therefore **cannot** live in the filter — it must happen at
3063
+ **classification time** in the aggregator/parser, exactly like the #44 synthetic
3064
+ INSERT-target → `kind='derived'` emission change (s18.1). A `CREATE [OR REPLACE]
3065
+ TEMP/TRANSIENT TABLE tmp_x AS SELECT …` whose target is a scratch object should be
3066
+ emitted `kind='derived'` at `_build_file_rows` time, the same place #44 already
3067
+ rewrites node identity. 492/495 of the leak (`tmp_*`/`temp_*`) are this CTAS-into-
3068
+ scratch shape — addressable by classifying on the parsed CTAS-into-temp statement
3069
+ kind (DDL-derived structural truth), **not** on the name prefix.
3070
+ - **Non-goal for the ticket:** the ~575 unqualified single-token nodes. They are a
3071
+ genuine mix of real bare tables and CTE aliases; auto-classifying them risks
3072
+ hiding real physical tables from the default surface (a false-negative worse than
3073
+ the current cosmetic leak). They stay `kind='table'` until a structural signal
3074
+ (CTE membership, CTAS target) is available — never a name guess.
3075
+
3076
+ **Risk/reward.** Reward: cleaner default `analyze` surface, ~22% fewer scratch
3077
+ intermediates leaking; closes the visible tail of #38. Risk: medium — must classify
3078
+ on parsed statement structure (TEMP/TRANSIENT CTAS target), and must not reclassify
3079
+ a real `tmp_`-named physical table that is read by a non-CTAS statement; needs a guard
3080
+ asserting a real table named `tmp_*` is still surfaced. Effort: medium (one emission
3081
+ site, reuses #44 plumbing, plus a behavioural guard). Below the open HIGH §3.x items
3082
+ and Finding 19.2 in the ranking. Recorded here; no plan stub drafted yet (pull when
3083
+ a lineage-coverage sprint next touches `_build_file_rows`).
3084
+
3085
+ ### 19.2 [RESOLVED in v1.2.2] `file:line='?'` on downstream terminal sinks
3086
+
3087
+ **Measurement.** The #45.1 fix rebinds a result's location from its **outgoing**
3088
+ `COLUMN_LINEAGE` edge ("where is this column used as a source"). For *downstream*
3089
+ traces this means terminal sinks render `?`: a sink has no outgoing edge, so
3090
+ `min(q.start_line)`/`min(q.file_path)` are NULL. Corpus measurement: of 36,892
3091
+ distinct columns reachable as a downstream result, **29,827 (81%) are sinks → render
3092
+ `file:line='?'`**. The fix populates only the locatable 19%. Upstream is unaffected
3093
+ (an upstream source legitimately has an outgoing edge). The TC6 guard asserts non-null
3094
+ `file:line` on one specific *located* case, so it passed despite the 81%.
3095
+
3096
+ **Decision: KEEP — pick up next.** This is a genuine bug, **not** deliberate
3097
+ semantics. Analysis tied to #45.1's own intent:
3098
+ - #45.1 chose the outgoing edge so a *source*'s location reads as "where it is
3099
+ produced/consumed as a source" — correct for the **upstream** direction. The same
3100
+ pattern was copied verbatim into the **downstream** query
3101
+ ([`analyze.py`](src/sqlcg/cli/commands/analyze.py) lines 134–137, fallback 146–149):
3102
+ `OPTIONAL MATCH (dst)-[dstedge]->()`. For a downstream *result*, the natural,
3103
+ user-meaningful location is the query that **produced** the column — i.e. the
3104
+ **incoming** edge `()-[dstedge]->(dst)`, not the outgoing one.
3105
+ - **Key lever (confirmed in code):** every one of the 36,892 columns has an incoming
3106
+ `COLUMN_LINEAGE` edge by definition — that is how it became a downstream result —
3107
+ and that producing query carries a real `start_line`/`file_path`. Flipping the two
3108
+ downstream `OPTIONAL MATCH` arrows from `(dst)-[dstedge]->()` to
3109
+ `()-[dstedge]->(dst)` binds location from the producing query, turning ~81% of `?`
3110
+ rows into a real `file:line`. Upstream stays on the outgoing edge unchanged.
3111
+
3112
+ **Risk/reward.** Reward: HIGH — ~81% of downstream result rows flip from `?` to a
3113
+ real `file:line` on a corpus this shape. Effort: LOW — a ~2-line arrow flip in two
3114
+ downstream queries (primary + bare-name fallback), no schema/index/perf change, no
3115
+ parser change. Risk: LOW and bounded — only the downstream direction; upstream and
3116
+ the kind-filter (s18.3) untouched. The one trap: TC6 passes today on a located case,
3117
+ so it does **not** catch the regression — the ticket must add a guard asserting a
3118
+ **terminal sink** (no outgoing edge) renders a real `file:line`, not `?`.
3119
+ Plan stub drafted: [`fix_downstream_sink_location.md`](plan/fix_downstream_sink_location.md).
3120
+ Suggested release: patch (1.2.2) or fold into the next lineage minor.
3121
+
3122
+ **RESOLVED — shipped in v1.2.2 (bundled with v1.2.1; see §18 bundled-release note).**
3123
+ Both downstream queries in [`analyze.py`](src/sqlcg/cli/commands/analyze.py) (primary
3124
+ L134, bare-name fallback L146) now bind location from the **incoming**
3125
+ `()-[dstedge:COLUMN_LINEAGE]->(dst)` edge — the producing query — so terminal sinks
3126
+ carry a real `file:line`. Upstream (L77/L89) keeps the outgoing-edge binding, unchanged.
3127
+ Guarded by the TC6b terminal-sink test in
3128
+ [`test_user_surface_recall_guard.py`](tests/integration/test_user_surface_recall_guard.py),
3129
+ demonstrably RED on master before the fix.
3130
+
3131
+ ### 19.3 Cross-finding note
3132
+
3133
+ Both findings share the v1.2.1 root theme: the *graph store* is correct (edges intact,
3134
+ kinds mostly right); the defects live in the **graph→user presentation layer**
3135
+ (filter classification for 19.1, location binding for 19.2) — the same blind-spot
3136
+ class as #38/#40 where anchors traversing raw edges pass while the user-facing query
3137
+ path is wrong. Any guard for either must drive the **user-facing `analyze` path**,
3138
+ not raw `COLUMN_LINEAGE` traversal.
3139
+
3140
+ ### 19.4 [KEEP — LOW] TC6b guard reconstructs the query instead of running `analyze downstream`
3141
+
3142
+ Raised by the plan-reviewer on the §19.2 sink fix. The TC6b terminal-sink guard's helper
3143
+ `_downstream_filtered_query()` in
3144
+ [`test_user_surface_recall_guard.py`](tests/integration/test_user_surface_recall_guard.py)
3145
+ **reconstructs the downstream Cypher query string** rather than invoking
3146
+ `analyze.downstream()` end-to-end. It therefore can drift from the production query if
3147
+ [`analyze.py`](src/sqlcg/cli/commands/analyze.py) changes and the helper is not updated in
3148
+ lockstep — exactly the §19.3 blind-spot (a guard not driving the true user-facing path).
3149
+ The fix held for v1.2.2 (the helper mirrors the shipped query), but the divergence risk is
3150
+ structural. **Future hardening:** replace the string-reconstruction helper with a
3151
+ `CliRunner`-based guard that runs the real `analyze downstream` command and asserts the
3152
+ rendered output. LOW priority; below §19.1 and the open §3.x items. No plan stub drafted.
3153
+
3154
+ ## 20. v1.3.0 — Single-Writer Queue + RO→RW Escalation (#29 / #28) (2026-06-04)
3155
+
3156
+ Implements the long-deferred §18.4 "#28 server read-only-by-default + reindex escalation"
3157
+ item. Plan: [`fix_issue_29_single_writer_queue.md`](plan/fix_issue_29_single_writer_queue.md).
3158
+ Branch `feat/fix-issue-29-single-writer-queue`, 8 commits (T-01…T-08). Test suite
3159
+ 1009 passed / 7 skipped / 1 xfailed; pyright clean; ruff clean.
3160
+
3161
+ ### 20.1 What shipped (plan-compliance verdict)
3162
+
3163
+ The five phases landed as planned. The hard invariants were honoured **in the
3164
+ control-socket / drain path**:
3165
+
3166
+ - **B1 (no co-existence window)** — Steps 2.2 (drain task + escalation primitive) and 2.3
3167
+ (retire the inline `reindex` op) landed in **one commit** (`62c19cd`). The `reindex` and
3168
+ `index` socket-op handlers in [`server.py`](src/sqlcg/server/server.py) `_control_socket_task`
3169
+ enqueue onto `WriterQueue` and never call `backend_ref()`; the drain task is the sole
3170
+ backend consumer, resolving the backend under `backend_lock` via `escalate_to_rw`.
3171
+ - **B2 (shutdown vs. drain ordering)** — `_stop_watcher`
3172
+ ([`server.py`](src/sqlcg/server/server.py) L394-410) sets `shutdown_requested` then
3173
+ acquires `backend_lock` **before** `shutdown_backend()`; `de_escalate_to_ro`
3174
+ ([`writer.py`](src/sqlcg/server/writer.py)) skips the RO reopen when the event is set.
3175
+ Both guarantees (a)+(b) present.
3176
+ - **B3 (status framing atomic)** — server-side `status` framing (Step 4.1) and the CLI
3177
+ recv-exactly parse (Step 4.2) shipped in one commit (`af2c85d`); `mcp_stop`'s
3178
+ `s.recv(128)` and the unframed `stop` reply are untouched.
3179
+ - **W3 (`from=null`)** — the `reindex` op stores `from_sha=None`; the drain resolves the
3180
+ stored SHA via `rw.get_indexed_sha()` and HEAD via `git rev-parse`, with an actionable
3181
+ "no prior index" error on a never-indexed DB. The CLI refusal was removed.
3182
+ - **W5 (`done:true` sentinel)** — both server emit and CLI recv loops key off
3183
+ `done == true`, not EOF; failure relays `ok:false, done:true, error:…`.
3184
+ - **W6 (`find_lock_holder` public)** — promoted in `kuzu_backend.py`, in-module call site
3185
+ updated, imported by `writer.py`.
3186
+ - **W7 (drain exception handler)** — the drain's `except Exception` clears `_active`
3187
+ (`mark_active_failed`), relays an `ok:false,done:true` terminal frame to waiters, and the
3188
+ loop survives; `EscalationLockError` keeps its dedicated C3 path with the ERROR log.
3189
+
3190
+ ### 20.2 Deviation 1 — MCP `index_repo` tool inline escalation (FLAGGED — follow-up required)
3191
+
3192
+ **What the developer added (not in the plan):** a `_serving_ro` flag +
3193
+ `_get_or_escalate_rw` / `_de_escalate_to_ro_from_tool` helpers in
3194
+ [`tools.py`](src/sqlcg/server/tools.py) so the MCP `index_repo` **tool** (invoked over the
3195
+ stdio transport, distinct from the CLI `index` socket op) escalates RO→RW inline when the
3196
+ server is serving RO.
3197
+
3198
+ **Verdict: the intent is consistent with the single-writer model, but the implementation
3199
+ violates the plan's central invariant and leaks the write lock on failure. Track as a
3200
+ v1.3.x follow-up, not a release blocker (the happy path works and tests are green), but
3201
+ it must be fixed before it is relied on under concurrency.**
3202
+
3203
+ Three concrete problems:
3204
+
3205
+ 1. **Escalation outside `backend_lock` (breaks B1/OD-7).** The plan's load-bearing invariant
3206
+ is "once escalation exists, **no op may operate on a backend resolved outside
3207
+ `backend_lock`**; the only backend consumer is the drain task." The MCP `index_repo` tool
3208
+ escalates and writes with **no lock held** — a second path that can swap
3209
+ `tools._backend` concurrently with a `query` op or a drain. This is exactly the class of
3210
+ bug B1 was written to prevent.
3211
+ 2. **RW-lock leak on the failure path.** `_de_escalate_to_ro_from_tool` is called only on the
3212
+ success branch ([`tools.py`](src/sqlcg/server/tools.py) L613-614), **not in a `finally`**.
3213
+ Any exception during `index_repo` / `upsert_node` / `run_read` / `upsert_edge` re-raises
3214
+ (L634) with the backend still RW — the exclusive write lock stays held, blocking all
3215
+ subsequent routed reads until the server is restarted. (Contrast the drain path, which
3216
+ de-escalates in a `finally`.)
3217
+ 3. **Dead `_set_backend_lock` / `_backend_lock`.** `_set_backend_lock`
3218
+ ([`tools.py`](src/sqlcg/server/tools.py) L117) and the `_backend_lock` global are
3219
+ **defined but never called** — a zero-value stub that looks like the intended lock plumbing
3220
+ for the tool escalation but was never wired. This is the CLAUDE.md "every new method must
3221
+ have a grep-confirmed call site" rule unmet, and the "function defined but never invoked"
3222
+ smell.
3223
+
3224
+ **Follow-up (recommended):** route the MCP `index_repo` tool write through the same
3225
+ `WriterQueue`/drain as the CLI `index` op (so there is genuinely one writer), or — minimally —
3226
+ acquire `backend_lock` for the escalation and move the de-escalation into a `finally`. There
3227
+ is **no test** for this path today (no test references `_get_or_escalate_rw` / `_serving_ro`),
3228
+ so the leak and the lock-bypass are unguarded. A regression test asserting (a) the tool
3229
+ escalates under the lock and (b) a raising `index_repo` de-escalates back to RO is required
3230
+ with the fix.
3231
+
3232
+ ### 20.3 Deviation 2 — Step 4.3 "queued behind N" display (ACCEPTED, minor)
3233
+
3234
+ The plan's Step 4.3 specified that a client queued behind other work prints
3235
+ `queued behind: <op> (done/total files) — position N` before attaching to live progress.
3236
+ The developer implemented the `done:true` terminal sentinel and the progress-bar attach
3237
+ fully, but **omitted the "queued behind … position N" pre-active display**: the CLI receives
3238
+ the `{queued:true, position:N}` frame and the subsequent progress frames, but the queued
3239
+ frame is silently consumed (it carries no `files_total`, so the progress loop renders
3240
+ nothing for it) — see [`index.py`](src/sqlcg/cli/commands/index.py) L307-311.
3241
+
3242
+ **Verdict: ACCEPTED as a cosmetic shortfall.** The functional contract (enqueue, attach,
3243
+ stream progress, terminate on `done:true`, exit non-zero on `ok:false`) is intact; only the
3244
+ human-readable "you are behind N others" hint is missing. The `position` is already on the
3245
+ wire, so completing it is a one-line render in the existing loop. Track as a polish item,
3246
+ not a correctness gap.
3247
+
3248
+ ### 20.4 Test-coverage gaps vs. the plan's Test Strategy (FLAGGED)
3249
+
3250
+ The plan's Test Strategy enumerated several **deterministic** integration tests that were
3251
+ not implemented as test functions, even though
3252
+ [`test_single_writer_queue.py`](tests/integration/test_single_writer_queue.py)'s module
3253
+ docstring lists B2 / W3 among its coverage (the docstring overstates what is asserted):
3254
+
3255
+ - **B2 stop-mid-drain** (in-flight write commits; `tools._backend is None` after shutdown,
3256
+ no reopen-after-shutdown) — listed in the docstring, **no test function**.
3257
+ - **W3** arg-level assertion (`resync_changed(from=stored SHA, to=HEAD)`) and the
3258
+ never-indexed "no prior index" error — **no test** (the drain code is present and exercised
3259
+ indirectly by Scenario A, but the W3-specific arg/branch assertions are missing).
3260
+ - **W7** drain-body-raises → `_active` cleared + `ok:false,done:true` terminal frame + loop
3261
+ survives — **no test**.
3262
+ - **Reads block during a drain** (OD-1, deterministic `anyio.Event` ordering) — **no test**.
3263
+ - **B1 structural guard** (the `reindex` op body contains no pre-lock `backend_ref()`,
3264
+ mirroring the perf-invariant guard discipline) — **no test**.
3265
+ - **Escalation transition on real Kuzu** (second in-process RO holder forces
3266
+ `EscalationLockError`) — **no test** (only the injected-`opener` unit tests cover retry).
3267
+
3268
+ What **is** covered: fresh-DB init, stale-schema refusal (Step 1.4), `db reset` refusal
3269
+ (Step 3.4), framed `status` + `writer_queue` shape (Steps 4.1/4.2), metrics persisted +
3270
+ `SQLCG_METRICS=0` suppression (Step 4.5), the escalation retry/backoff + ERROR-log unit tests
3271
+ (Steps 1.2/4.4), the coalescing rules (Step 2.1), and the drain-runs-`resync_changed`-once
3272
+ invariant guard (Scenario A in `test_reindex_via_server.py`).
3273
+
3274
+ **Verdict:** these are real plan deviations (the named deterministic guards were specified
3275
+ and not delivered), but the shipped behaviour is exercised indirectly and the suite is green,
3276
+ so they are **follow-up hardening**, not a ship blocker. They should be filed alongside the
3277
+ Deviation-1 fix — the B2/W7/reads-block tests in particular guard the exact concurrency
3278
+ contract that Deviation 1 currently bypasses.
@@ -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.3.0
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 |
@@ -62,6 +62,13 @@ sqlcg index [OPTIONS] PATH
62
62
 
63
63
  Index SQL files in a directory.
64
64
 
65
+ When a server is live on this DB, the index is routed through the server's
66
+ control socket so the DB is never opened directly (avoids lock contention).
67
+ Use --detach to enqueue and return immediately (fire-and-forget).
68
+
69
+ With no server live, falls back to the direct-write path unchanged
70
+ (zero-config small-repo invariant).
71
+
65
72
  Schema aliases (staging schema → canonical schema) can be configured in
66
73
  .sqlcg.toml under sqlcg.schema_aliases, e.g. da_tmp = "da".
67
74
 
@@ -71,13 +78,16 @@ Schema aliases (staging schema → canonical schema) can be configured in
71
78
  | --- | --- | --- | --- | --- | --- |
72
79
  | --dialect, -d | TEXT | No | No | | SQL dialect (or 'auto' to read from .sqlcg.toml) |
73
80
  | --dbt-manifest | PATH | No | No | | Path to dbt manifest |
74
- | --timeout-per-file | INTEGER | No | No | 5 | Timeout per file in seconds |
81
+ | --timeout-per-file | INTEGER | No | No | 10 | Timeout per file in seconds |
75
82
  | --buffer-pool-size | INTEGER | No | No | 0 | KuzuDB buffer pool size in MB (0 = default). Set to 256-512 on memory-constrained machines. |
76
83
  | --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
84
  | --no-ddl | BOOLEAN | No | No | False | Skip table-node upserts for DDL-only files |
78
85
  | --quiet, -q | BOOLEAN | No | No | False | Suppress summary console output |
86
+ | --verbose, -v | BOOLEAN | No | No | False | Print parse warnings to stderr instead of log file |
79
87
  | --debug | BOOLEAN | No | No | False | Show detailed log output during indexing |
80
88
  | --profile / --no-profile | BOOLEAN | No | No | False | Emit per-stage timing after indexing |
89
+ | --include-working-tree | BOOLEAN | No | No | False | Index the working tree including uncommitted changes. Marks freshness as 'indexed with working-tree changes'. |
90
+ | --detach | BOOLEAN | No | No | False | When routing through a live server, return immediately after enqueueing (fire-and-forget). Default is to wait for the index to complete. |
81
91
 
82
92
  ## `sqlcg reindex`
83
93
 
@@ -106,7 +116,8 @@ build — run 'sqlcg db reset && sqlcg db init && sqlcg index <path>' to re-init
106
116
  | --dialect, -d | TEXT | No | No | | SQL dialect (or 'auto' to read from .sqlcg.toml) |
107
117
  | --quiet, -q | BOOLEAN | No | No | False | Suppress summary output |
108
118
  | --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 |
119
+ | --timeout-per-file | INTEGER | No | No | 10 | Per-file parse timeout in seconds |
120
+ | --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
121
 
111
122
  ## `sqlcg watch`
112
123
 
@@ -181,7 +192,10 @@ If no metrics database exists, prints a message and exits 0.
181
192
  sqlcg install [OPTIONS]
182
193
  ```
183
194
 
184
- Register sqlcg as an MCP server in Claude Code (~/.claude/settings.json).
195
+ Register sqlcg as an MCP server in Claude Code.
196
+
197
+ Runs ``claude mcp add -s user sql-code-graph <cmd> <args>`` when the
198
+ ``claude`` CLI is on PATH; otherwise writes ~/.claude.json directly.
185
199
 
186
200
  Also provisions a Claude skill file (SKILL.md) at the chosen location.
187
201
  Pass --scope project or --scope global to specify where the skill is written.
@@ -333,7 +347,7 @@ Find a table by name.
333
347
 
334
348
  | Option | Type | Required | Repeatable | Default | Description |
335
349
  | --- | --- | --- | --- | --- | --- |
336
- | _none_ | | | | | |
350
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
337
351
 
338
352
  ## `sqlcg find column`
339
353
 
@@ -347,7 +361,7 @@ Find a column by table.column reference.
347
361
 
348
362
  | Option | Type | Required | Repeatable | Default | Description |
349
363
  | --- | --- | --- | --- | --- | --- |
350
- | _none_ | | | | | |
364
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
351
365
 
352
366
  ## `sqlcg find pattern`
353
367
 
@@ -394,6 +408,8 @@ Trace upstream column lineage.
394
408
  | Option | Type | Required | Repeatable | Default | Description |
395
409
  | --- | --- | --- | --- | --- | --- |
396
410
  | --depth | INTEGER | No | No | 5 | Maximum traversal depth |
411
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
412
+ | --include-intermediate | BOOLEAN | No | No | False | Include CTE/derived intermediate nodes |
397
413
 
398
414
  ## `sqlcg analyze downstream`
399
415
 
@@ -408,6 +424,8 @@ Trace downstream column lineage.
408
424
  | Option | Type | Required | Repeatable | Default | Description |
409
425
  | --- | --- | --- | --- | --- | --- |
410
426
  | --depth | INTEGER | No | No | 5 | Maximum traversal depth |
427
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
428
+ | --include-intermediate | BOOLEAN | No | No | False | Include CTE/derived intermediate nodes |
411
429
 
412
430
  ## `sqlcg analyze impact`
413
431
 
@@ -421,7 +439,7 @@ Show all queries impacted by a table.
421
439
 
422
440
  | Option | Type | Required | Repeatable | Default | Description |
423
441
  | --- | --- | --- | --- | --- | --- |
424
- | _none_ | | | | | |
442
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
425
443
 
426
444
  ## `sqlcg analyze failures`
427
445
 
@@ -431,6 +449,9 @@ sqlcg analyze failures [OPTIONS]
431
449
 
432
450
  List files that failed to parse, with their dominant cause (E-code bucket).
433
451
 
452
+ Valid --cause buckets (from highest to lowest severity):
453
+ timeout, E8, E3, E2, E5, E1, qualify_failed, func_fallback, pure_ddl_skip.
454
+
434
455
  Requires a graph indexed with sqlcg >= v3 (schema version 3). Re-index
435
456
  with 'sqlcg db reset && sqlcg index <path>' if the graph was built with
436
457
  an earlier version.
@@ -439,7 +460,7 @@ an earlier version.
439
460
 
440
461
  | Option | Type | Required | Repeatable | Default | Description |
441
462
  | --- | --- | --- | --- | --- | --- |
442
- | --cause | TEXT | No | No | | Filter by E-code bucket (e.g. E5, timeout) |
463
+ | --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
464
  | --limit | INTEGER | No | No | 100 | Maximum rows to return |
444
465
 
445
466
  ## `sqlcg analyze unused`
@@ -455,6 +476,7 @@ Find tables with no query references.
455
476
  | Option | Type | Required | Repeatable | Default | Description |
456
477
  | --- | --- | --- | --- | --- | --- |
457
478
  | --threshold | INTEGER | No | No | 0 | Minimum reference count threshold |
479
+ | --raw | BOOLEAN | No | No | False | Disable noise filtering on results |
458
480
 
459
481
  ## `sqlcg mcp`
460
482
 
@@ -471,6 +493,9 @@ MCP server commands
471
493
  | `setup` | Print or write MCP server config JSON. |
472
494
  | `start` | Start the MCP server. |
473
495
  | `best-practices` | Print MCP tool best-practices (the fact/heuristic boundary). |
496
+ | `status` | Print server status JSON (connects to control socket). |
497
+ | `stop` | Stop the running MCP server gracefully. |
498
+ | `restart` | Stop the server. The client (editor) must respawn. |
474
499
 
475
500
  ## `sqlcg mcp setup`
476
501
 
@@ -480,6 +505,11 @@ sqlcg mcp setup [OPTIONS]
480
505
 
481
506
  Print or write MCP server config JSON.
482
507
 
508
+ --print (default): print the JSON snippet for manual insertion.
509
+ --write: write to ~/.claude.json under mcpServers.user (the correct path
510
+ for Claude Code — not settings.json, which Claude Code does not read
511
+ for MCP servers).
512
+
483
513
  ### Options
484
514
 
485
515
  | Option | Type | Required | Repeatable | Default | Description |
@@ -517,6 +547,77 @@ that have not installed the skill.
517
547
  | --- | --- | --- | --- | --- | --- |
518
548
  | _none_ | | | | | |
519
549
 
550
+ ## `sqlcg mcp status`
551
+
552
+ ```bash
553
+ sqlcg mcp status [OPTIONS]
554
+ ```
555
+
556
+ Print server status JSON (connects to control socket).
557
+
558
+ Returns JSON with fields: running, pid, db_path, indexed_sha, head_sha,
559
+ stale_by_commits, connected_clients, uptime, writer_queue when a server
560
+ is live.
561
+
562
+ The status response is length-prefixed framed (v1.3.0, B3) so large
563
+ writer_queue payloads are received in full — the client uses the
564
+ recv-exactly makefile+readline+read(n) pattern, NOT a single recv(4096).
565
+
566
+ When no server is found: {"running": false}.
567
+ When the PID file exists with a live process but the socket is unavailable:
568
+ {"running": true, "degraded": "socket unavailable", ...}.
569
+
570
+ R3 (stale socket): if the socket file exists but the server is not
571
+ responding (ConnectionRefusedError / FileNotFoundError), falls through
572
+ to the PID-file probe — never hangs or errors on a dead socket.
573
+
574
+ ### Options
575
+
576
+ | Option | Type | Required | Repeatable | Default | Description |
577
+ | --- | --- | --- | --- | --- | --- |
578
+ | _none_ | | | | | |
579
+
580
+ ## `sqlcg mcp stop`
581
+
582
+ ```bash
583
+ sqlcg mcp stop [OPTIONS]
584
+ ```
585
+
586
+ Stop the running MCP server gracefully.
587
+
588
+ Sends a ``stop`` op via the control socket; waits up to 5 s for the
589
+ socket file to disappear (confirming clean exit). Falls back to SIGTERM
590
+ on the PID-file PID if the socket is unavailable.
591
+
592
+ R3 (stale socket): ``ConnectionRefusedError`` / ``FileNotFoundError`` are
593
+ caught — never hangs on a dead socket.
594
+
595
+ ### Options
596
+
597
+ | Option | Type | Required | Repeatable | Default | Description |
598
+ | --- | --- | --- | --- | --- | --- |
599
+ | _none_ | | | | | |
600
+
601
+ ## `sqlcg mcp restart`
602
+
603
+ ```bash
604
+ sqlcg mcp restart [OPTIONS]
605
+ ```
606
+
607
+ Stop the server. The client (editor) must respawn.
608
+
609
+ v1.1 cannot re-parent an editor-spawned stdio process. This command
610
+ stops the current server and prints guidance for the user to restart
611
+ the MCP server via their editor's MCP configuration.
612
+
613
+ True auto-restart (re-parenting stdio) is deferred to v1.2.
614
+
615
+ ### Options
616
+
617
+ | Option | Type | Required | Repeatable | Default | Description |
618
+ | --- | --- | --- | --- | --- | --- |
619
+ | _none_ | | | | | |
620
+
520
621
  ## `sqlcg git`
521
622
 
522
623
  ```bash
@@ -542,6 +643,8 @@ Install git hooks for sqlcg integration.
542
643
  Writes a post-checkout hook that triggers incremental resync after branch switches
543
644
  and a post-merge hook that triggers resync after pulls/merges.
544
645
  Idempotent: running multiple times produces one hook entry per hook.
646
+ The hooks embed the absolute path of the installing sqlcg binary so version skew
647
+ between the installed binary and the hook command is avoided.
545
648
 
546
649
  ### Options
547
650