better-code-review-graph 3.28.0__tar.gz → 3.29.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.
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.claude-plugin/plugin.json +1 -1
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/CHANGELOG.md +17 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/PKG-INFO +1 -1
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/pyproject.toml +1 -1
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/server.json +2 -2
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/cli.py +6 -3
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/graph.md +1 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/summarizer.py +190 -9
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/tools.py +5 -1
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/conftest.py +14 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_cli_control_plane.py +34 -1
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_summarize_graph_dispatch.py +11 -1
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_summarizer.py +238 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.agents/skills/build-graph/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.agents/skills/review-delta/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.agents/skills/review-pr/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.coderabbit.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.dockerignore +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/CODEOWNERS +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/review-learnings.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/rulesets/main.json +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/workflows/cd.yml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.github/workflows/ci.yml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.gitignore +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.infisical.json +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.jules/bolt.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.jules/sentinel.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.pre-commit-config.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/AGENTS.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/BREAKING_CHANGES.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/CLAUDE.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/CODE_OF_CONDUCT.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/CONTRIBUTING.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/Dockerfile +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/LICENSE +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/LICENSE-MIT +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/NOTICE +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/README.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/SECURITY.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/alembic.ini +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/docs/HANDOVER.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/hooks/_graph_state.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/hooks/branch-sync-check.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/hooks/hooks.json +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/hooks/stale-graph-check.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/README +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/env.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/script.py.mako +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/versions/001_baseline.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/versions/002_summary_columns.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/versions/003_federation.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/versions/004_security_tags.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/versions/005_temporal_columns.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/migrations/versions/006_commits_table.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/renovate.json +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/rules/heuristic/cwe-22-path-traversal.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/rules/heuristic/cwe-78-shell-command-sink.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/rules/heuristic/cwe-89-sql-string-format.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/rules/heuristic/cwe-95-eval-on-input.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/rules/heuristic/hardcoded-secret.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/rules/semgrep/curated.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/scripts/benchmark_row_deserialization.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/scripts/enforce-commit.sh +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/scripts/preserve-diacritics.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/scripts/test_preserve_diacritics.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/semantic-release.toml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/skills/impact-audit/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/skills/onboard-repo/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/skills/refactor-check/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/skills/review-delta/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/skills/review-pr/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/skills/security-sweep/SKILL.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/smithery.yaml +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/__init__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/__main__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/config.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/credential_state.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/__init__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/config.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/query.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/recipes.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/review.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/docs/security.md +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/embeddings.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/exporter.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/federation.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/graph.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/importer.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/incremental.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/parser.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/py.typed +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/reranker.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/__init__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/_types.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/fallback.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/go.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/java.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/python.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/rust.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/resolver/typescript.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/security/__init__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/security/heuristic.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/security/semgrep_engine.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/server.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/temporal.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/src/better_code_review_graph/xpia.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/__init__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/conftest_e2e.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/Sample.cs +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/SampleJava.java +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/callback_refs.js +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/caller_example.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/multi_call_example.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.c +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.cpp +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.kt +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.php +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.rb +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.sol +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample.swift +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample_go.go +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample_python.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample_rust.rs +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/sample_typescript.ts +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/fixtures/test_sample.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/integration/__init__.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/integration/test_multi_user_remote.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_alembic_baseline.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_as_of_diff.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_auto_resolve_ambiguity.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_bare_suffix_correctness.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_callers_not_found_reason.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_callers_of_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_cli.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_cli_contracts.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_cohere_dimensions.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_commits_migration.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_coverage_gaps.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_credential_state.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_credential_state_dehosted.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_crg_protocol_harness.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_custom_local_model.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_dynamic_dispatch_hints.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_e2e.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_embedder_retry_classification.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_embedding_migration.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_embeddings.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_embeddings_extra.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_export_graph_dispatch.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_export_graph_dispatch_path_traversal.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_exporter.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_federation.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_federation_migration.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_federation_smoke.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_full_live.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_g6_ux_status_accuracy.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_get_docs_section_error_path.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_get_review_context_no_diff.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_graph.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_graph_batch_fetch.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_graph_extra.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_graph_get_edges_by_targets_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_graph_stats_error_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_handle_not_found_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_help_recipes.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_heuristic.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_home_isolation.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_http_mcp_e2e.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_hull_cells.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_impact_payload_truncation.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_implements_edges.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_import_graph_dispatch.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_importer.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_incremental.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_incremental_extra.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_kind_filter_optimization.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_kinds_error_path.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_list_graph_stats_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_list_kinds_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_live_mcp.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_local_reranker.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_lookup_node_directly.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_multi_user.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_multilang.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_no_stale_package_name.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_oci_publication_sunset.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_oserror_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_parser.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_parser_extra.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_parser_grammar_failure.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_parser_source_text.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_path_resolution_fallback.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_payload_estimation.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_per_sub_graph_db.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_performance_n1.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_performance_n1_embeddings.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_phase_3_smoke.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_plugin_assets.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_pre_2_0_backup.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_renamed_in_diff.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_renamed_in_diff_exception.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_repo_filter.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_repo_filter_batching.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_repo_id_persistence.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolve_search_candidates.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_dispatcher.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_fallback.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_go.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_java.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_python.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_rust.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_resolver_typescript.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_response_header.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_response_payload_honesty.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_review_delta_line_shifts.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_review_languages_filter.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_reviewer_summary.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_search_edges_by_target_names.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_search_keyword_warning.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_security_injection.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_security_path_traversal.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_security_scan_batching.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_security_tags_migration.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_security_tool.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_semgrep_engine.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_server.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_server_help_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_serverinfo_version.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_setup_tool.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_shares_state_edges.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_spot_check.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_sql_security.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_stdio_direct.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_summary_schema.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_temporal.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_temporal_migration.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_tested_by_edges.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_tools.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_tools_build_error_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_tools_full.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_tools_get_docs_section_coverage.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_typescript_coverage_gaps.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_v17_coverage_gaps.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_xpia.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_xpia_adversarial.py +0 -0
- {better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/uv.lock +0 -0
{better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/.claude-plugin/plugin.json
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-code-review-graph",
|
|
3
3
|
"description": "Knowledge graph for token-efficient code reviews -- semantic search and call-graph resolution across your codebase.",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.29.0",
|
|
5
5
|
"userConfig": {
|
|
6
6
|
"EMBEDDING_MODELS": {
|
|
7
7
|
"type": "string",
|
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v3.29.0 (2026-09-27)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- Rank summarize queue with deterministic order and jev advisory batches
|
|
10
|
+
([`2739dd8`](https://github.com/n24q02m/crg/commit/2739dd85386bb6080b97380902e021bc84bd4fba))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v3.28.1 (2026-09-27)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
- Enforce no-auth loopback guard against config-sourced host
|
|
18
|
+
([#1102](https://github.com/n24q02m/crg/pull/1102),
|
|
19
|
+
[`bf4afc3`](https://github.com/n24q02m/crg/commit/bf4afc338aa7443959fc522cfa980ef583d7e26d))
|
|
20
|
+
|
|
21
|
+
|
|
5
22
|
## v3.28.0 (2026-09-27)
|
|
6
23
|
|
|
7
24
|
### Code Style
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "better-code-review-graph"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.29.0"
|
|
8
8
|
description = "Token-efficient code review knowledge graph: semantic search and call-graph resolution."
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/n24q02m/crg.git",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.29.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "pypi",
|
|
13
13
|
"identifier": "better-code-review-graph",
|
|
14
|
-
"version": "3.
|
|
14
|
+
"version": "3.29.0",
|
|
15
15
|
"runtimeHint": "uvx",
|
|
16
16
|
"runtimeArgs": [
|
|
17
17
|
"--python",
|
|
@@ -537,18 +537,21 @@ def _handle_server_start(args: argparse.Namespace) -> int:
|
|
|
537
537
|
from .server import serve_main
|
|
538
538
|
|
|
539
539
|
os.environ["MCP_TRANSPORT"] = "http"
|
|
540
|
-
# run_http()
|
|
541
|
-
|
|
540
|
+
# run_http() bind precedence: MCP_HOST env > settings.server.host; --port
|
|
541
|
+
# hands off via MCP_PORT (honored at server.py:986-989).
|
|
542
|
+
settings = load_instance_settings()
|
|
542
543
|
if args.host:
|
|
543
544
|
os.environ["MCP_HOST"] = args.host
|
|
544
545
|
if args.port:
|
|
545
546
|
os.environ["MCP_PORT"] = str(args.port)
|
|
546
547
|
# Security property carried over from the wp2-wip line: an
|
|
547
548
|
# unauthenticated (no-auth) listener must never bind off-loopback.
|
|
549
|
+
# Resolve the EFFECTIVE bind host the way run_http does.
|
|
550
|
+
host = args.host or os.environ.get("MCP_HOST") or settings.server.host
|
|
548
551
|
if (
|
|
549
552
|
host
|
|
550
553
|
and host not in {"localhost", "127.0.0.1", "::1"}
|
|
551
|
-
and
|
|
554
|
+
and settings.server.auth == "no-auth"
|
|
552
555
|
):
|
|
553
556
|
print(
|
|
554
557
|
"crg refuses to start: auth = 'no-auth' only permits loopback "
|
|
@@ -148,6 +148,7 @@ graph is unchanged. A provider key alone does not enable summaries.
|
|
|
148
148
|
**Cost cap + caching:**
|
|
149
149
|
- Default cap is 500 LLM calls per invocation. Tune with `max_nodes` for tighter budgets.
|
|
150
150
|
- Repeat invocations skip nodes whose `source_hash` + full selected model match. `summary_provider` stores the `provider/model` identity, so switching models within one provider invalidates stale summaries too.
|
|
151
|
+
- The work queue has a deterministic base order (`ORDER BY id`). When the host configures a `[models.jev_score]` cell, the pending queue is additionally re-prioritized by one advisory jev call per ~50-node batch (never one call per node); the result carries a `jev_ranking` receipt (`used`, `batches`, `ranked_nodes`, `failopen_reason`). Any jev failure is fail-open: the queue keeps its base order and only the receipt notes it.
|
|
151
152
|
|
|
152
153
|
**Example:**
|
|
153
154
|
```json
|
|
@@ -8,10 +8,15 @@ fallback. The full selected model is persisted in ``summary_provider`` so
|
|
|
8
8
|
switching models invalidates the cached summary.
|
|
9
9
|
|
|
10
10
|
Dispatch goes through hull-core's ``OpenAICompatClient`` (async httpx, one
|
|
11
|
-
client per batch). The batch queue
|
|
12
|
-
|
|
13
|
-
§7 K4
|
|
14
|
-
|
|
11
|
+
client per batch). The batch queue is a single ``SELECT ... LIMIT ?`` over
|
|
12
|
+
Function nodes with a deterministic base ordering (``ORDER BY id``, spec
|
|
13
|
+
§7 K4). On top of that base order, an optional jev ranking layer re-orders
|
|
14
|
+
the pending (cache-miss) queue in ~50-node batches — exactly one advisory
|
|
15
|
+
``jev_score`` call per batch, never one call per node — and is strictly
|
|
16
|
+
fail-open: any jev unavailability (cell not configured, call error,
|
|
17
|
+
unparseable reply) leaves the queue in base order and is recorded in the
|
|
18
|
+
run receipt only. The queue never changes the schema, only the processing
|
|
19
|
+
order.
|
|
15
20
|
"""
|
|
16
21
|
|
|
17
22
|
from __future__ import annotations
|
|
@@ -19,6 +24,7 @@ from __future__ import annotations
|
|
|
19
24
|
import asyncio
|
|
20
25
|
import hashlib
|
|
21
26
|
import logging
|
|
27
|
+
import re
|
|
22
28
|
from dataclasses import dataclass
|
|
23
29
|
from typing import Any
|
|
24
30
|
|
|
@@ -81,6 +87,22 @@ def summary_cell() -> Any:
|
|
|
81
87
|
return cell if cell.configured else None
|
|
82
88
|
|
|
83
89
|
|
|
90
|
+
def jev_score_cell() -> Any:
|
|
91
|
+
"""The configured jev_score cell (host-only key), or ``None``.
|
|
92
|
+
|
|
93
|
+
Unlike :func:`summary_cell`, resolution failures also yield ``None``:
|
|
94
|
+
the ranking layer is advisory (spec §7 K4 fail-open), so an unreadable
|
|
95
|
+
config may only ever disable prioritization, never the queue itself.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
from .config import resolve_cells
|
|
99
|
+
|
|
100
|
+
cell = resolve_cells().get("jev_score")
|
|
101
|
+
except Exception: # config problems must only disable ranking
|
|
102
|
+
return None
|
|
103
|
+
return cell if cell is not None and cell.configured else None
|
|
104
|
+
|
|
105
|
+
|
|
84
106
|
# ---------------------------------------------------------------------------
|
|
85
107
|
# Single-node LLM summarization
|
|
86
108
|
# ---------------------------------------------------------------------------
|
|
@@ -144,6 +166,31 @@ def _auth_mode() -> str:
|
|
|
144
166
|
# Default cap on per-run LLM calls. Override per-call via the max_nodes parameter.
|
|
145
167
|
DEFAULT_MAX_NODES_PER_RUN = 500
|
|
146
168
|
|
|
169
|
+
# jev ranking layer (spec §7 K4): the pending queue is re-ordered in batches
|
|
170
|
+
# of this many nodes, with exactly one advisory jev_score call per batch.
|
|
171
|
+
JEV_BATCH_SIZE = 50
|
|
172
|
+
|
|
173
|
+
# Per-node excerpt handed to the ranking call, and the cap on the recorded
|
|
174
|
+
# fail-open reason so a chatty provider error cannot bloat the receipt.
|
|
175
|
+
_JEV_EXCERPT_CHARS = 160
|
|
176
|
+
_FAILOPEN_REASON_MAX_CHARS = 200
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass(frozen=True)
|
|
180
|
+
class JevRankingReceipt:
|
|
181
|
+
"""Provenance of the jev ranking layer for one batch_summarize run.
|
|
182
|
+
|
|
183
|
+
``used=True`` means jev re-ordered the queue. ``used=False`` with a
|
|
184
|
+
``None`` ``failopen_reason`` means the layer was disabled (no jev cell,
|
|
185
|
+
or a single-node queue); a non-``None`` reason means jev was attempted,
|
|
186
|
+
failed, and the queue fell back to base order.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
used: bool
|
|
190
|
+
batches: int = 0 # ranked batches; one jev call each
|
|
191
|
+
ranked_nodes: int = 0 # nodes whose queue position jev decided
|
|
192
|
+
failopen_reason: str | None = None # why ranking was skipped, if attempted
|
|
193
|
+
|
|
147
194
|
|
|
148
195
|
@dataclass(frozen=True)
|
|
149
196
|
class BatchSummarizeResult:
|
|
@@ -154,6 +201,132 @@ class BatchSummarizeResult:
|
|
|
154
201
|
skipped_no_provider: bool = False # True iff no chat cell is configured
|
|
155
202
|
provider: str | None = None # provider used (None if skipped)
|
|
156
203
|
errors: int = 0 # nodes where the chat call raised; counted, batch continues
|
|
204
|
+
jev_ranking: JevRankingReceipt | None = None # queue ranking provenance
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# jev queue ranking (spec §7 K4 — advisory, fail-open)
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
_JEV_RANK_PROMPT_PREFIX = (
|
|
212
|
+
"You are prioritizing a code-review summarization queue. Rank the "
|
|
213
|
+
"numbered functions below by value to a code reviewer: core logic, "
|
|
214
|
+
"complex control flow and public entry points first; trivial accessors, "
|
|
215
|
+
"constants and one-line wrappers last.\n"
|
|
216
|
+
"Reply with ONLY a JSON array of the indices in priority order, e.g. "
|
|
217
|
+
"[3, 1, 0, 2]. Every index must appear exactly once.\n\nFunctions:\n"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Scan the reply for numeric tokens (mnemo-wp4-writer jev precedent:
|
|
221
|
+
# ``[+-]?\\d*\\.\\d+|\\d+``); a reply without any number raises instead of
|
|
222
|
+
# guessing an order.
|
|
223
|
+
_NUMBER_RE = re.compile(r"[+-]?\d*\.\d+|\d+")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _parse_jev_ranking(content: str, batch_size: int) -> list[int]:
|
|
227
|
+
"""Parse jev's priority order out of its reply.
|
|
228
|
+
|
|
229
|
+
Follows the mnemo-wp4-writer precedent: scan for numeric tokens, raise
|
|
230
|
+
when the reply carries no numbers at all. Indices outside
|
|
231
|
+
``range(batch_size)`` and duplicates are ignored; an incomplete
|
|
232
|
+
permutation raises so the caller can fail open to base order.
|
|
233
|
+
"""
|
|
234
|
+
tokens = _NUMBER_RE.findall(content)
|
|
235
|
+
if not tokens:
|
|
236
|
+
raise ValueError(f"jev ranking reply has no numeric tokens: {content[:80]!r}")
|
|
237
|
+
order: list[int] = []
|
|
238
|
+
seen: set[int] = set()
|
|
239
|
+
for token in tokens:
|
|
240
|
+
index = int(float(token))
|
|
241
|
+
if 0 <= index < batch_size and index not in seen:
|
|
242
|
+
seen.add(index)
|
|
243
|
+
order.append(index)
|
|
244
|
+
if len(order) != batch_size:
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"jev ranking incomplete: got {len(order)} of {batch_size} indices"
|
|
247
|
+
)
|
|
248
|
+
return order
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
async def _jev_rank_batch(
|
|
252
|
+
client: Any,
|
|
253
|
+
batch: list[tuple[int, NodeNeedingSummary]],
|
|
254
|
+
) -> list[tuple[int, NodeNeedingSummary]]:
|
|
255
|
+
"""One advisory jev call: return ``batch`` re-ordered by review priority."""
|
|
256
|
+
listing = _JEV_RANK_PROMPT_PREFIX
|
|
257
|
+
for position, (_, node) in enumerate(batch):
|
|
258
|
+
condensed = " ".join(node.source_text.split())[:_JEV_EXCERPT_CHARS]
|
|
259
|
+
listing += (
|
|
260
|
+
f"{position}. ({len(node.source_text.splitlines())} lines) {condensed}\n"
|
|
261
|
+
)
|
|
262
|
+
content = await client.chat(
|
|
263
|
+
[{"role": "user", "content": listing}],
|
|
264
|
+
max_tokens=1024,
|
|
265
|
+
reasoning={"exclude": True},
|
|
266
|
+
)
|
|
267
|
+
order = _parse_jev_ranking(content, len(batch))
|
|
268
|
+
return [batch[index] for index in order]
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
async def _jev_rank_queue(
|
|
272
|
+
client: Any,
|
|
273
|
+
pending: list[tuple[int, NodeNeedingSummary]],
|
|
274
|
+
batch_size: int = JEV_BATCH_SIZE,
|
|
275
|
+
) -> tuple[list[tuple[int, NodeNeedingSummary]], int]:
|
|
276
|
+
"""Rank the whole pending queue, one jev call per ``batch_size`` chunk.
|
|
277
|
+
|
|
278
|
+
Any failure propagates: the caller discards partial rankings and keeps
|
|
279
|
+
the queue in base order (fail-open is all-or-nothing per run).
|
|
280
|
+
"""
|
|
281
|
+
ordered: list[tuple[int, NodeNeedingSummary]] = []
|
|
282
|
+
batches = 0
|
|
283
|
+
for start in range(0, len(pending), batch_size):
|
|
284
|
+
batch = pending[start : start + batch_size]
|
|
285
|
+
ordered.extend(await _jev_rank_batch(client, batch))
|
|
286
|
+
batches += 1
|
|
287
|
+
return ordered, batches
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _maybe_jev_rank(
|
|
291
|
+
pending: list[tuple[int, NodeNeedingSummary]],
|
|
292
|
+
) -> tuple[JevRankingReceipt, list[tuple[int, NodeNeedingSummary]]]:
|
|
293
|
+
"""Re-order ``pending`` via the advisory jev ranking layer (spec §7 K4).
|
|
294
|
+
|
|
295
|
+
Fail-open: an unconfigured cell returns the queue unchanged silently;
|
|
296
|
+
any ranking failure logs a warning and returns the queue unchanged with
|
|
297
|
+
the reason on the receipt. Only a fully successful pass re-orders.
|
|
298
|
+
"""
|
|
299
|
+
if len(pending) < 2: # nothing to prioritize
|
|
300
|
+
return JevRankingReceipt(used=False), pending
|
|
301
|
+
|
|
302
|
+
jev_cell = jev_score_cell()
|
|
303
|
+
if jev_cell is None:
|
|
304
|
+
return JevRankingReceipt(used=False), pending
|
|
305
|
+
|
|
306
|
+
async def _rank() -> tuple[list[tuple[int, NodeNeedingSummary]], int]:
|
|
307
|
+
client = OpenAICompatClient(jev_cell, auth_mode=_auth_mode())
|
|
308
|
+
try:
|
|
309
|
+
return await _jev_rank_queue(client, pending, JEV_BATCH_SIZE)
|
|
310
|
+
finally:
|
|
311
|
+
await client.aclose()
|
|
312
|
+
|
|
313
|
+
try:
|
|
314
|
+
ordered, batches = asyncio.run(_rank())
|
|
315
|
+
except Exception as exc: # ranking is advisory only — never block the queue
|
|
316
|
+
logger.warning("jev ranking failed; queue keeps base order: %s", exc)
|
|
317
|
+
return (
|
|
318
|
+
JevRankingReceipt(
|
|
319
|
+
used=False,
|
|
320
|
+
batches=0,
|
|
321
|
+
ranked_nodes=0,
|
|
322
|
+
failopen_reason=str(exc)[:_FAILOPEN_REASON_MAX_CHARS],
|
|
323
|
+
),
|
|
324
|
+
pending,
|
|
325
|
+
)
|
|
326
|
+
return (
|
|
327
|
+
JevRankingReceipt(used=True, batches=batches, ranked_nodes=len(ordered)),
|
|
328
|
+
ordered,
|
|
329
|
+
)
|
|
157
330
|
|
|
158
331
|
|
|
159
332
|
def batch_summarize(
|
|
@@ -165,8 +338,11 @@ def batch_summarize(
|
|
|
165
338
|
|
|
166
339
|
Iteration scope: at most ``max_nodes`` Function-kind nodes whose
|
|
167
340
|
``source_text`` is non-null, selected by one ``SELECT ... LIMIT ?``
|
|
168
|
-
with
|
|
169
|
-
|
|
341
|
+
with deterministic base ordering (``ORDER BY id`` — spec §7 K4). When
|
|
342
|
+
the host configures a jev_score cell, the pending (cache-miss) queue is
|
|
343
|
+
additionally re-ordered by one advisory jev call per ~50-node batch;
|
|
344
|
+
any jev failure leaves the queue in base order (fail-open) and is
|
|
345
|
+
recorded in :attr:`BatchSummarizeResult.jev_ranking`. For each candidate:
|
|
170
346
|
|
|
171
347
|
- If stored summary + ``summary_provider`` + ``source_hash`` all match
|
|
172
348
|
the selected model + freshly-computed source hash, it's a cache hit
|
|
@@ -204,7 +380,8 @@ def batch_summarize(
|
|
|
204
380
|
# because it copies large `source_text` columns.
|
|
205
381
|
cursor = store._conn.execute(
|
|
206
382
|
"SELECT id, source_text, source_hash, summary, summary_provider FROM nodes "
|
|
207
|
-
"WHERE kind='Function' AND source_text IS NOT NULL
|
|
383
|
+
"WHERE kind='Function' AND source_text IS NOT NULL "
|
|
384
|
+
"ORDER BY id LIMIT ?",
|
|
208
385
|
(max_nodes,),
|
|
209
386
|
)
|
|
210
387
|
|
|
@@ -240,6 +417,8 @@ def batch_summarize(
|
|
|
240
417
|
)
|
|
241
418
|
)
|
|
242
419
|
|
|
420
|
+
ranking, pending = _maybe_jev_rank(pending)
|
|
421
|
+
|
|
243
422
|
if pending:
|
|
244
423
|
generated, errors = asyncio.run(
|
|
245
424
|
_summarize_pending(store, cell, cache_provider, pending)
|
|
@@ -253,6 +432,7 @@ def batch_summarize(
|
|
|
253
432
|
skipped_no_provider=False,
|
|
254
433
|
provider=cache_provider,
|
|
255
434
|
errors=errors,
|
|
435
|
+
jev_ranking=ranking,
|
|
256
436
|
)
|
|
257
437
|
|
|
258
438
|
|
|
@@ -264,8 +444,9 @@ async def _summarize_pending(
|
|
|
264
444
|
) -> tuple[int, int]:
|
|
265
445
|
"""Run the pending queue through one shared hull client.
|
|
266
446
|
|
|
267
|
-
Sequential,
|
|
268
|
-
the
|
|
447
|
+
Sequential, queue order preserved (base ``ORDER BY id`` order,
|
|
448
|
+
optionally re-ranked by the jev layer). Each failure is logged and
|
|
449
|
+
counted; the batch continues (fail-open per-node, spec §7).
|
|
269
450
|
"""
|
|
270
451
|
client = OpenAICompatClient(cell, auth_mode=_auth_mode())
|
|
271
452
|
generated = 0
|
|
@@ -16,6 +16,7 @@ import hashlib
|
|
|
16
16
|
import json
|
|
17
17
|
import logging
|
|
18
18
|
import time
|
|
19
|
+
from dataclasses import asdict
|
|
19
20
|
from pathlib import Path
|
|
20
21
|
from typing import Any
|
|
21
22
|
|
|
@@ -2933,7 +2934,9 @@ def summarize_graph_dispatch(
|
|
|
2933
2934
|
max_nodes: Max LLM calls per invocation. Default 500.
|
|
2934
2935
|
|
|
2935
2936
|
Returns:
|
|
2936
|
-
Dict with status, generated, cached, errors, provider,
|
|
2937
|
+
Dict with status, generated, cached, errors, provider, jev_ranking,
|
|
2938
|
+
summary. ``jev_ranking`` carries the queue-ranking receipt (None on
|
|
2939
|
+
the skipped path); see ``summarizer.JevRankingReceipt``.
|
|
2937
2940
|
"""
|
|
2938
2941
|
from . import summarizer
|
|
2939
2942
|
|
|
@@ -2962,6 +2965,7 @@ def summarize_graph_dispatch(
|
|
|
2962
2965
|
"generated": result.generated,
|
|
2963
2966
|
"cached": result.cached,
|
|
2964
2967
|
"errors": result.errors,
|
|
2968
|
+
"jev_ranking": asdict(result.jev_ranking) if result.jev_ranking else None,
|
|
2965
2969
|
"summary": (
|
|
2966
2970
|
f"Summarized {result.generated} new + {result.cached} cached Function "
|
|
2967
2971
|
f"node(s) via {result.provider}"
|
|
@@ -14,6 +14,20 @@ import pytest
|
|
|
14
14
|
REAL_HOME = Path.home()
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
@pytest.fixture(autouse=True)
|
|
18
|
+
def _jev_ranking_disabled_by_default(monkeypatch):
|
|
19
|
+
"""Default every test to jev-ranking-off (spec §7 K4).
|
|
20
|
+
|
|
21
|
+
Ranking is an advisory layer driven by the host's ``[models.jev_score]``
|
|
22
|
+
cell; without this opt-out a developer/CI box with that cell configured
|
|
23
|
+
would leak live jev calls into unrelated queue tests. Tests exercising
|
|
24
|
+
the ranking layer re-patch ``summarizer.jev_score_cell`` themselves.
|
|
25
|
+
"""
|
|
26
|
+
monkeypatch.setattr(
|
|
27
|
+
"better_code_review_graph.summarizer.jev_score_cell", lambda: None
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
17
31
|
def pytest_addoption(parser):
|
|
18
32
|
"""Add --setup and --browser CLI options for E2E tests."""
|
|
19
33
|
parser.addoption("--setup", choices=["relay", "env", "plugin"], default="env")
|
{better_code_review_graph-3.28.0 → better_code_review_graph-3.29.0}/tests/test_cli_control_plane.py
RENAMED
|
@@ -73,7 +73,13 @@ def test_db_path_prints_graph_db(capsys) -> None:
|
|
|
73
73
|
assert out.endswith("graph.db")
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def test_server_start_forces_http_transport(capsys, monkeypatch) -> None:
|
|
76
|
+
def test_server_start_forces_http_transport(capsys, tmp_path, monkeypatch) -> None:
|
|
77
|
+
cfg = tmp_path / "cfg"
|
|
78
|
+
cfg.mkdir()
|
|
79
|
+
(cfg / "config.toml").write_text(
|
|
80
|
+
'[server]\nhost = "127.0.0.1"\nport = 61000\nauth = "no-auth"\n'
|
|
81
|
+
)
|
|
82
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(cfg))
|
|
77
83
|
monkeypatch.delenv("MCP_TRANSPORT", raising=False)
|
|
78
84
|
monkeypatch.delenv("MCP_HOST", raising=False)
|
|
79
85
|
monkeypatch.delenv("MCP_PORT", raising=False)
|
|
@@ -110,6 +116,7 @@ def test_server_start_refuses_no_auth_off_loopback(
|
|
|
110
116
|
)
|
|
111
117
|
monkeypatch.setenv("CRG_CONFIG_DIR", str(cfg))
|
|
112
118
|
monkeypatch.delenv("MCP_TRANSPORT", raising=False)
|
|
119
|
+
monkeypatch.delenv("MCP_HOST", raising=False)
|
|
113
120
|
serve_called = False
|
|
114
121
|
|
|
115
122
|
def must_not_serve() -> None:
|
|
@@ -124,6 +131,32 @@ def test_server_start_refuses_no_auth_off_loopback(
|
|
|
124
131
|
assert serve_called is False
|
|
125
132
|
|
|
126
133
|
|
|
134
|
+
def test_server_start_refuses_no_auth_config_host_off_loopback(
|
|
135
|
+
capsys, tmp_path, monkeypatch
|
|
136
|
+
) -> None:
|
|
137
|
+
"""Bypass regression: no --host/MCP_HOST, config-sourced host binds."""
|
|
138
|
+
cfg = tmp_path / "cfg"
|
|
139
|
+
cfg.mkdir()
|
|
140
|
+
(cfg / "config.toml").write_text(
|
|
141
|
+
'[server]\nhost = "0.0.0.0"\nport = 61000\nauth = "no-auth"\n'
|
|
142
|
+
)
|
|
143
|
+
monkeypatch.setenv("CRG_CONFIG_DIR", str(cfg))
|
|
144
|
+
monkeypatch.delenv("MCP_TRANSPORT", raising=False)
|
|
145
|
+
monkeypatch.delenv("MCP_HOST", raising=False)
|
|
146
|
+
serve_called = False
|
|
147
|
+
|
|
148
|
+
def must_not_serve() -> None:
|
|
149
|
+
nonlocal serve_called # noqa: F841
|
|
150
|
+
serve_called = True # pragma: no cover
|
|
151
|
+
|
|
152
|
+
monkeypatch.setattr("better_code_review_graph.server.serve_main", must_not_serve)
|
|
153
|
+
rc = _run(capsys, "server", "start")
|
|
154
|
+
err = capsys.readouterr().err
|
|
155
|
+
assert rc == 2
|
|
156
|
+
assert "no-auth" in err and "loopback" in err
|
|
157
|
+
assert serve_called is False
|
|
158
|
+
|
|
159
|
+
|
|
127
160
|
def test_unknown_subcommand_is_rc2(capsys) -> None:
|
|
128
161
|
rc = _run(capsys, "nope")
|
|
129
162
|
err = capsys.readouterr().err
|
|
@@ -5,7 +5,10 @@ from __future__ import annotations
|
|
|
5
5
|
from unittest.mock import patch
|
|
6
6
|
|
|
7
7
|
from better_code_review_graph.graph import GraphStore
|
|
8
|
-
from better_code_review_graph.summarizer import
|
|
8
|
+
from better_code_review_graph.summarizer import (
|
|
9
|
+
BatchSummarizeResult,
|
|
10
|
+
JevRankingReceipt,
|
|
11
|
+
)
|
|
9
12
|
from better_code_review_graph.tools import summarize_graph_dispatch
|
|
10
13
|
|
|
11
14
|
|
|
@@ -40,6 +43,7 @@ def test_dispatch_returns_ok_with_counts_on_success(tmp_path, monkeypatch):
|
|
|
40
43
|
skipped_no_provider=False,
|
|
41
44
|
provider="gemini",
|
|
42
45
|
errors=0,
|
|
46
|
+
jev_ranking=JevRankingReceipt(used=False),
|
|
43
47
|
)
|
|
44
48
|
|
|
45
49
|
with patch("better_code_review_graph.tools._get_store") as mock_get_store:
|
|
@@ -59,6 +63,12 @@ def test_dispatch_returns_ok_with_counts_on_success(tmp_path, monkeypatch):
|
|
|
59
63
|
assert result["generated"] == 3
|
|
60
64
|
assert result["cached"] == 1
|
|
61
65
|
assert result["errors"] == 0
|
|
66
|
+
assert result["jev_ranking"] == {
|
|
67
|
+
"used": False,
|
|
68
|
+
"batches": 0,
|
|
69
|
+
"ranked_nodes": 0,
|
|
70
|
+
"failopen_reason": None,
|
|
71
|
+
}
|
|
62
72
|
assert "3 new" in result["summary"]
|
|
63
73
|
assert "1 cached" in result["summary"]
|
|
64
74
|
assert "gemini" in result["summary"]
|