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.
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/PKG-INFO +21 -8
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/README.md +20 -7
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/docs/cli.md +18 -10
- sql_code_graph-1.5.1/plan/version-parity-and-restart.md +390 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/pyproject.toml +1 -1
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/__init__.py +1 -1
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/install.py +56 -43
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/mcp.py +58 -10
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/main.py +34 -3
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/models.py +3 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/server.py +9 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/tools.py +11 -1
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_install.py +80 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_mcp_control.py +130 -0
- sql_code_graph-1.5.1/tests/unit/test_version_parity.py +148 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/uv.lock +1 -1
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/api-documenter.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/architect-planner.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/architect-reviewer.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/code-reviewer.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/developer.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/plan-reviewer.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.claude/agents/sprint-planner.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/benchmark.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/e2e-tests.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/release.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.github/workflows/test.yml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.gitignore +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.pre-commit-config.yaml +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/.sqlcgignore +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/ARCHITECTURE_REVIEW.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/CHANGELOG.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/CLAUDE.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/docs/AIRBNB_PARSE_REPORT.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/e2e_firstuser_report.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/e2e_run_20260528_101413.output +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/main.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/WORKFLOW.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/blueprint.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/bundle_claude_skill.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/diff-impact-producer-file.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_34_unused_presentation_segregation.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_35_external_downstream_injection.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_F2_bundle_claude_skill.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/feature_kuzu_to_duckdb_migration.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_downstream_sink_location.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_dynamic_table_parsing.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_expand_qualify_perf.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_firstuser_findings.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_issue29_live_test_followups.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/fix_schema_case_mismatch.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/hygiene_config_path_and_survivors.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/investigation_e5_e4.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/issue-38-backfill-cte-bridge.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/living_codebase_resync.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/schema_comparison_with_schema.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/schema_comparison_without_schema.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/sprint_08_changelogs_fullindex.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/sprint_08_fullcorpus_index.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/measurements/sprint_pool_300s_plan.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/parse_diagnostics.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/parsing_errors_experiment.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/snowflake_en_test_suite.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_01.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_01_deployment_pypi.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_02.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_02_v0.3.0_core.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_03.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_03_v0.3.1_postmortem.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_04_column_lineage.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_04_column_lineage_fix.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_05_star_resolution.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_06_lineage_coverage.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_07_open_ecodes.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_07_perf_and_live_test.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_08_perf_upsert.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_09_lineage_coverage.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_10_anchor_tools.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_11_v1.0.2_bugfix.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_12_v1.1.0.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_13_v1.1.0_cluster_b.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sprint_3.1_postmortem.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/sqlcg.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/trust_layer.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1.1.0_cluster_b_provenance_trust.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1.1.0_live_graph_freshness.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1.1.1_batch_upsert_perf.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_1_2_bugfix.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_1_3_union_cte_star.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_2_0_read_proxy.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/plan/v1_2_1_bugfix.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/profile.html +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/pyrightconfig.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/scripts/collect_parse_errors.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/scripts/generate_cli_docs.sh +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/__main__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/analyze.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/db.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/find.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/gain.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/git.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/index.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/reindex.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/report.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/uninstall.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/cli/commands/watch.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/config.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/duckdb_backend.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/freshness.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/graph_db.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/jobs.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/queries.cypher +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/queries.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/queries.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/schema.cypher +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/core/schema.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/dbt_adapter.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/error_classify.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/git_delta.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/indexer.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/pool.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/walker.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/indexer/watcher.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/lineage/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/lineage/aggregator.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/lineage/schema_resolver.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/metrics/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/metrics/store.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/ansi_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/base.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/bigquery_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/postgres_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/registry.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/snowflake_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/parsers/tsql_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/control.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/exceptions.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/noise_filter.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/read_client.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/skill.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/server/writer.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/hashing.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/ignore.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/src/sqlcg/utils/logging.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/adversarial/200_join.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/adversarial/500_union.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/bench_indexer.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/conftest.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/case_normalization.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/colon_cast.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/colon_reserved_word.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/copy_into.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/create_procedure.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/identifier_dynamic.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/lateral_flatten.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/qualify.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/scripting_block.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/golden_corpus/snowflake/three_part.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q01.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q02.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q03.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q04.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/benchmarks/tpch/q05.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/conftest.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_F2_skill_install_e2e.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_airbnb_e2e.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_cli_index.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_git_hook_install.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_golden_lineage.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_mcp_lifecycle.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_mcp_tools.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_parse_diagnostics_cli.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_star_resolution_e2e.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/e2e/test_watch.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/dim_hosts_cleansed.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/dim_listings_cleansed.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/fct_reviews.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/mart_fullmoon_reviews.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/raw_hosts.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/raw_listings.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/raw_reviews.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/src_hosts.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/src_listings.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/airbnb/src_reviews.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/bigquery/.gitkeep +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/duckdb_parity/kuzu_reference.json +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/jaffle_shop/customers.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/jaffle_shop/orders.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/jaffle_shop/raw_orders.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/snowflake/base_tables.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/snowflake/reports.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/snowflake/views.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/ddl_src.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/ddl_tgt.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/etl_alias_star.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/star_corpus/etl_star.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/synthetic/base_tables.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/synthetic/reports.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/fixtures/synthetic/views.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/snowflake/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/snowflake/test_insert_select.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_T34_presentation_segregation.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_T35_external_consumers.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_analyze_case_fold.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_anchor_tools.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_backfill_impact_consistency.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_bare_column_cte_lineage.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_bulk_upsert.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_case_split_seed_regression.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_column_lineage_e2e.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cross_file_lineage.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cte_recall_guard.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cte_schema_alias_guard.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_cte_source_node_invariant.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_dialect_auto_resolution.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_dialect_matrix.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_duckdb_parity.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_e36_xfile_regression_guard.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_freshness_mcp.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_hygiene_config_root_reconciliation.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_indexer_batching.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_indexer_commits.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_indexer_to_graph.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_live_anchors.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_mvcc_rebuild.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_parse_diagnostics.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_pr1_confidence_reason.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_pr2_source_location.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_pr3_kind_tagging.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_read_via_server.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_readonly_under_lock.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_reindex_via_server.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_resync.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_single_writer_queue.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_star_resolution.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_temp_table_lineage.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_union_cte_star_recall_guard.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_user_surface_recall_guard.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/integration/test_v141_surface_guards.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/perf/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/perf/test_perf.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E10/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E11/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/e12_json_path.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/e12_lateral_flatten.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E12/test_e12.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E13/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E14/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E15/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/e16_merge.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/e16_merge_delete.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E16/test_e16.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E17/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/e18_decode.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/e18_iff_decode.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/e18_nvl2.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E18/test_e18.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E19/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/e2_expr_alias.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/e2_function_alias.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/e2_multiply_alias.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E2/test_e2.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E20/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/e21_alias_forward_ref.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/e21_three_level_chain.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E21/test_e21.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E22/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/e23_stored_proc.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/e23_stored_proc_multi.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E23/test_e23.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E24/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/e25_cross_db.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/e25_two_part.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/test_e25.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E25/test_e25_full_id.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E26/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/e27_nested_udf.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/e27_udf.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E27/test_e27.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E28/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E29/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/e3_alter_table.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/e3_create_sequence.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/e3_ddl_only.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E3/test_e3.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E30/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E31/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E32/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E33/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E34/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E35/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/e36_temp_multi_use.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/e36_temp_table.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/test_e36.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E36/test_e36_xfile.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E37/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E38/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_execute_immediate.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_if_not_exists.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_unexpected_token.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/e4_unpivot.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E4/test_e4.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/e5_cte_missing_source.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/e5_multi_cte.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/e5_nested_cte.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/test_e5.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E5/test_e5_cte_projection.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/e8_dynamic_sources.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/e8_seq_nextval.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/e8_uuid.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E8/test_e8.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E9/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/fixture_sum_absent_cross_schema.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/fixture_sum_case_when.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/fixture_sum_present_source.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_aggregates/test_e_aggregates.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_date_aliased.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_date_unaliased.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_datediff_unaliased.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/fixture_year_unaliased.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/E_date_functions/test_e_date_functions.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/README.md +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_etl.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_omloopsnelheid.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_semantic.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/fixture_source.sql +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/test_anchor_ma_aantal_op_order.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/anchors/test_anchor_omloopsnelheid.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/conftest.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/snowflake/test_plan_review_gates.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/snowflake/__init__.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/snowflake/test_scripting_noise.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_BugB_escalation_uses_init_path.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_BugC_hook_upgrade.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_F2_install_skill.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_F2_skill_render.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_F2_uninstall_skill.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_01_qualify_once.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_02_ddl_skip.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_04_subprocess_isolate.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T09_06_log_verbosity.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_T35_config_external_consumers.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_aggregator.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_aggregator_skip.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_analyze_case_fold.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_base_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_branch_monitor.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_bulk_upsert_invariant.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_canonical_target_resolution.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_cli.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_cli_help.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_closure_depth.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_column_lineage_wiring.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_config.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_data_models.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_db_info.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_dominant_cause.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_duckdb_backend.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_duckdb_backend_shared.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_find_cmd.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_firstuser_findings.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_freshness_helper.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_gain_ratio.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_git_delta.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_git_hooks.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_git_hooks_notify.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_graph_backend.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_graph_completeness_invariant.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_hard_kill_pool.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_hygiene_config_warning.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_include_working_tree.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_index_cmd.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_index_flags.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_index_progress.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_indexer_progress.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_indexer_quality.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_install_message.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_jobs.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_judgement.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_lineage_conversion.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_literal_column_skip.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_mcp_best_practices.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_mcp_stdio_smoke.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_metrics.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_noise_filter.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_parse_file_dependency_filter.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_parse_quality.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_parser.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_perf_scaling_guard.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_pr07_observability.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_queries_loader.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_read_client.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_resolve_pass2_passes_dependency_filter.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_schema_resolver.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_server.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_snowflake_strip_alter_set_tag.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_sprint_06_t04_t05.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_star_resolution_unit.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_star_schema_unit.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_submit_feedback.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_subprocess_isolate.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_t02_expression_name_extraction.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_t03_ddl_skip.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_timeout_cancel.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_tools_hints.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_tools_warnings.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_uninstall.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_unqualified_fallback.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_upsert_batch_invariant.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_walker.py +0 -0
- {sql_code_graph-1.4.3 → sql_code_graph-1.5.1}/tests/unit/test_watcher.py +0 -0
- {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.
|
|
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
|
-
> **
|
|
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
|
|
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
|
-
- `
|
|
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
|
-
| `
|
|
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
|
|
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
|
-
|
|
271
|
-
|
|
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
|
-
> **
|
|
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
|
|
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
|
-
- `
|
|
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
|
-
| `
|
|
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
|
|
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
|
-
|
|
235
|
-
|
|
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,
|
|
551
|
-
stale_by_commits, connected_clients, uptime, writer_queue
|
|
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
|
-
|
|
581
|
-
|
|
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).
|