descry-codegraph 0.3.3__tar.gz → 1.0.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.
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/.github/workflows/ci.yml +7 -1
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/CHANGELOG.md +495 -2
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/CLAUDE.md +12 -11
- descry_codegraph-1.0.0/CONTRACT.md +93 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/PKG-INFO +64 -29
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/README.md +59 -20
- descry_codegraph-1.0.0/SECURITY.md +66 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/claude-skill/README.md +1 -1
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/claude-skill/descry/SKILL.md +21 -16
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/claude-skill/descry/references/cli-reference.md +11 -10
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/claude-skill/descry/references/mcp-tools.md +7 -7
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/claude-skill/descry/references/recipes.md +9 -6
- descry_codegraph-1.0.0/docs/CERTAINTY.md +61 -0
- descry_codegraph-1.0.0/docs/TOOLS.md +659 -0
- descry_codegraph-1.0.0/docs/UPGRADING.md +301 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/justfile +9 -5
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/pi-extension/descry-cli.py +8 -8
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/pi-extension/descry-tools.ts +12 -12
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/pyproject.toml +4 -4
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/__init__.py +1 -1
- descry_codegraph-1.0.0/src/descry/_answers.py +7 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/_comments.py +6 -12
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/_files.py +19 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/_graph.py +18 -6
- descry_codegraph-1.0.0/src/descry/_parse_cache.py +209 -0
- descry_codegraph-1.0.0/src/descry/_parsing.py +3161 -0
- descry_codegraph-1.0.0/src/descry/_reads.py +287 -0
- descry_codegraph-1.0.0/src/descry/_stdlib.py +110 -0
- descry_codegraph-1.0.0/src/descry/_streams.py +11 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/clang_parser.py +241 -80
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/cli.py +207 -156
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/cross_lang.py +17 -77
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/dart_parser.py +27 -36
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/dotnet_parser.py +176 -108
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/embeddings.py +136 -89
- descry_codegraph-1.0.0/src/descry/generate.py +1470 -0
- descry_codegraph-1.0.0/src/descry/git_history.py +1517 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/go_parser.py +83 -73
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/handlers.py +1393 -974
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/java_parser.py +30 -45
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/mcp_server.py +171 -165
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/php_parser.py +55 -48
- descry_codegraph-1.0.0/src/descry/proto_parser.py +66 -0
- descry_codegraph-1.0.0/src/descry/python_parser.py +1030 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/query.py +694 -876
- descry_codegraph-1.0.0/src/descry/resolver.py +10074 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/ruby_parser.py +126 -92
- descry_codegraph-1.0.0/src/descry/rust_parser.py +2195 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapter.py +52 -38
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/clang.py +6 -30
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/dart.py +7 -58
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/dotnet.py +15 -70
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/go.py +19 -53
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/java.py +18 -73
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/php.py +18 -63
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/python.py +27 -43
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/ruby.py +5 -33
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/rust.py +12 -50
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/typescript.py +9 -55
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/cache.py +77 -219
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/parser.py +102 -194
- descry_codegraph-1.0.0/src/descry/scip/support.py +40 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/cpp.json +99 -141
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/dotnet.json +652 -1
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/go.json +49 -1
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/python.json +60 -14
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/ruby.json +649 -414
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/rust.json +2714 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/typescript.json +6 -1
- descry_codegraph-1.0.0/src/descry/ts_parser.py +6089 -0
- descry_codegraph-1.0.0/src/descry/web/server.py +803 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/web/web/index.html +188 -282
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/web/web/style.css +1 -70
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/conftest.py +12 -0
- descry_codegraph-1.0.0/tests/ground_truth/c/Makefile +2 -0
- descry_codegraph-1.0.0/tests/ground_truth/c/expected.json +14 -0
- descry_codegraph-1.0.0/tests/ground_truth/c/io.c +7 -0
- descry_codegraph-1.0.0/tests/ground_truth/c/io.h +2 -0
- descry_codegraph-1.0.0/tests/ground_truth/c/main.c +3 -0
- descry_codegraph-1.0.0/tests/ground_truth/c/order.c +5 -0
- descry_codegraph-1.0.0/tests/ground_truth/cpp/Makefile +2 -0
- descry_codegraph-1.0.0/tests/ground_truth/cpp/expected.json +17 -0
- descry_codegraph-1.0.0/tests/ground_truth/cpp/main.cpp +9 -0
- descry_codegraph-1.0.0/tests/ground_truth/cpp/models.hpp +25 -0
- descry_codegraph-1.0.0/tests/ground_truth/cpp/reader.hpp +9 -0
- descry_codegraph-1.0.0/tests/ground_truth/csharp/Gt.csproj +6 -0
- descry_codegraph-1.0.0/tests/ground_truth/csharp/Io.cs +15 -0
- descry_codegraph-1.0.0/tests/ground_truth/csharp/Models.cs +36 -0
- descry_codegraph-1.0.0/tests/ground_truth/csharp/Program.cs +9 -0
- descry_codegraph-1.0.0/tests/ground_truth/csharp/expected.json +17 -0
- descry_codegraph-1.0.0/tests/ground_truth/dart/bin/main.dart +8 -0
- descry_codegraph-1.0.0/tests/ground_truth/dart/expected.json +78 -0
- descry_codegraph-1.0.0/tests/ground_truth/dart/lib/io.dart +9 -0
- descry_codegraph-1.0.0/tests/ground_truth/dart/lib/models.dart +21 -0
- descry_codegraph-1.0.0/tests/ground_truth/dart/pubspec.yaml +3 -0
- descry_codegraph-1.0.0/tests/ground_truth/go/expected.json +17 -0
- descry_codegraph-1.0.0/tests/ground_truth/go/go.mod +3 -0
- descry_codegraph-1.0.0/tests/ground_truth/go/io.go +13 -0
- descry_codegraph-1.0.0/tests/ground_truth/go/main.go +7 -0
- descry_codegraph-1.0.0/tests/ground_truth/go/models.go +20 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/expected.json +19 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Base.java +11 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Io.java +7 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Main.java +9 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Order.java +7 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Reader.java +7 -0
- descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/User.java +15 -0
- descry_codegraph-1.0.0/tests/ground_truth/javascript/expected.json +91 -0
- descry_codegraph-1.0.0/tests/ground_truth/javascript/package.json +1 -0
- descry_codegraph-1.0.0/tests/ground_truth/javascript/src/io.js +14 -0
- descry_codegraph-1.0.0/tests/ground_truth/javascript/src/main.js +13 -0
- descry_codegraph-1.0.0/tests/ground_truth/javascript/src/models.js +28 -0
- descry_codegraph-1.0.0/tests/ground_truth/php/composer.json +1 -0
- descry_codegraph-1.0.0/tests/ground_truth/php/expected.json +86 -0
- descry_codegraph-1.0.0/tests/ground_truth/php/src/Io.php +19 -0
- descry_codegraph-1.0.0/tests/ground_truth/php/src/Models.php +38 -0
- descry_codegraph-1.0.0/tests/ground_truth/php/src/main.php +17 -0
- descry_codegraph-1.0.0/tests/ground_truth/python/app/__init__.py +0 -0
- descry_codegraph-1.0.0/tests/ground_truth/python/app/io.py +14 -0
- descry_codegraph-1.0.0/tests/ground_truth/python/app/main.py +13 -0
- descry_codegraph-1.0.0/tests/ground_truth/python/app/models.py +20 -0
- descry_codegraph-1.0.0/tests/ground_truth/python/expected.json +90 -0
- descry_codegraph-1.0.0/tests/ground_truth/python/pyproject.toml +2 -0
- descry_codegraph-1.0.0/tests/ground_truth/ruby/Gemfile +1 -0
- descry_codegraph-1.0.0/tests/ground_truth/ruby/expected.json +85 -0
- descry_codegraph-1.0.0/tests/ground_truth/ruby/lib/io.rb +16 -0
- descry_codegraph-1.0.0/tests/ground_truth/ruby/lib/main.rb +13 -0
- descry_codegraph-1.0.0/tests/ground_truth/ruby/lib/models.rb +26 -0
- descry_codegraph-1.0.0/tests/ground_truth/rust/Cargo.toml +4 -0
- descry_codegraph-1.0.0/tests/ground_truth/rust/expected.json +77 -0
- descry_codegraph-1.0.0/tests/ground_truth/rust/src/io.rs +11 -0
- descry_codegraph-1.0.0/tests/ground_truth/rust/src/main.rs +11 -0
- descry_codegraph-1.0.0/tests/ground_truth/rust/src/models.rs +42 -0
- descry_codegraph-1.0.0/tests/ground_truth/typescript/expected.json +87 -0
- descry_codegraph-1.0.0/tests/ground_truth/typescript/package.json +1 -0
- descry_codegraph-1.0.0/tests/ground_truth/typescript/src/io.ts +13 -0
- descry_codegraph-1.0.0/tests/ground_truth/typescript/src/main.ts +14 -0
- descry_codegraph-1.0.0/tests/ground_truth/typescript/src/models.ts +28 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_backtracking.py +19 -12
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_clang_parser.py +1 -2
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_cli.py +70 -27
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_config.py +63 -17
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_cross_lang.py +63 -33
- descry_codegraph-1.0.0/tests/test_docs.py +22 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_dotnet_parser.py +7 -7
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_embeddings.py +114 -8
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_generate.py +5336 -282
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_git_history.py +511 -122
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_go_parser.py +16 -30
- descry_codegraph-1.0.0/tests/test_ground_truth.py +171 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_handlers.py +539 -110
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_hardening.py +151 -26
- descry_codegraph-1.0.0/tests/test_interfaces.py +581 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_mcp.py +99 -13
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_non_regular_files.py +2 -2
- descry_codegraph-1.0.0/tests/test_parse_cache.py +227 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_query.py +390 -374
- descry_codegraph-1.0.0/tests/test_resolve_cache.py +233 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_ruby_parser.py +1 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip.py +136 -59
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_cache.py +120 -32
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_clang.py +10 -11
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_dart.py +7 -9
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_dotnet.py +3 -12
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_go.py +1 -8
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_java.py +16 -25
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_php.py +1 -10
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_scip_ruby.py +1 -10
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_stdlib.py +126 -7
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_web_server.py +264 -263
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_written_through.py +5 -5
- descry_codegraph-1.0.0/tools/docs/gen_tools.py +167 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/README.md +58 -22
- descry_codegraph-1.0.0/tools/stdlib/common.py +171 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_c.py +31 -58
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_cpp.py +188 -125
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_dart.py +25 -33
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_dotnet.py +113 -32
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_go.py +27 -20
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_java.py +8 -1
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_node.py +2 -2
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_php.py +6 -1
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_python.py +175 -50
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_ruby.py +89 -60
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_rust.py +208 -3
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tools/stdlib/gen_typescript.py +48 -33
- descry_codegraph-0.3.3/SECURITY.md +0 -27
- descry_codegraph-0.3.3/src/descry/_stdlib.py +0 -71
- descry_codegraph-0.3.3/src/descry/generate.py +0 -25636
- descry_codegraph-0.3.3/src/descry/git_history.py +0 -1771
- descry_codegraph-0.3.3/src/descry/scip/support.py +0 -70
- descry_codegraph-0.3.3/src/descry/web/server.py +0 -1089
- descry_codegraph-0.3.3/tests/test_interfaces.py +0 -296
- descry_codegraph-0.3.3/tools/stdlib/common.py +0 -76
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/.github/assets/01-landing.png +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/.github/workflows/release.yml +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/.gitignore +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/LICENSE +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/THIRD-PARTY-NOTICES.md +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/pi-extension/README.md +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/_env.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/__init__.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/__init__.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/_empty_environment.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/adapters/_java_compat.init.gradle +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/scip/pb2.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/c.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/dart.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/java.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/node.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/stdlib/php.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/src/descry/web/__init__.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/__init__.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/README.md +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/__init__.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/compare.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/corpus.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/metrics.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/registry.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/results/results-descry.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/eval/results/results-ripgrep.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/fixtures/small_graph.json +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_dart_parser.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_java_parser.py +0 -0
- {descry_codegraph-0.3.3 → descry_codegraph-1.0.0}/tests/test_php_parser.py +0 -0
|
@@ -24,10 +24,16 @@ jobs:
|
|
|
24
24
|
- run: ruff format --check src/ tests/ tools/
|
|
25
25
|
|
|
26
26
|
test:
|
|
27
|
-
runs-on:
|
|
27
|
+
runs-on: ${{ matrix.os }}
|
|
28
28
|
strategy:
|
|
29
29
|
matrix:
|
|
30
|
+
os: [ubuntu-latest]
|
|
30
31
|
python-version: ["3.11", "3.12", "3.13"]
|
|
32
|
+
# The unit suite and the interface tests on macOS too; Windows is
|
|
33
|
+
# best-effort, kept from regressing on a local VM.
|
|
34
|
+
include:
|
|
35
|
+
- os: macos-latest
|
|
36
|
+
python-version: "3.13"
|
|
31
37
|
steps:
|
|
32
38
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
33
39
|
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
@@ -2,9 +2,502 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Descry will be documented in this file.
|
|
4
4
|
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [semver](https://semver.org/) from 1.0.0.
|
|
6
6
|
|
|
7
|
-
## [
|
|
7
|
+
## [1.0.0] — 2026-10-29
|
|
8
|
+
|
|
9
|
+
### Breaking
|
|
10
|
+
|
|
11
|
+
- Every symbol-taking tool asks for `symbol` (a node ID or a name), where
|
|
12
|
+
some asked for `name` or `node_id`: MCP `descry_callers`, `descry_callees`,
|
|
13
|
+
`descry_quick`, `descry_evolution`, `descry_context`, `descry_flatten`; the
|
|
14
|
+
web's `?name=` / `?node_id=` / `?class_node_id=`; the CLI's positional
|
|
15
|
+
argument names. `docs/UPGRADING.md` lists the interface changes in full.
|
|
16
|
+
- Every failure is `{error, reason}` (and `choices` for a shared name) in
|
|
17
|
+
every interface, with `reason` one of `invalid`, `ambiguous`, `not_found`,
|
|
18
|
+
`busy`, `no_graph`, `unavailable`, `loading`, `failed`. Arguments an
|
|
19
|
+
interface cannot read as their type (the CLI's too, with `--json`),
|
|
20
|
+
unknown search languages and types (lower case, aliases such as `ts`
|
|
21
|
+
taken everywhere), an HTTP method no OpenAPI operation can be under, a
|
|
22
|
+
date git cannot read, and an empty symbol, search, query, file or method
|
|
23
|
+
name are `invalid` in every interface where some silently answered. The
|
|
24
|
+
web's own refusals (host, origin, `/api/source`) carry `reason` too.
|
|
25
|
+
- A name several definitions share is refused with every node ID to choose
|
|
26
|
+
from, by every tool but `search` and `impls`; a name no definition has is
|
|
27
|
+
`not_found` naming close matches. `callers` and `callees` no longer answer
|
|
28
|
+
for one close match, and their `fuzzy` field is removed.
|
|
29
|
+
- Structured answers carry IDs, counts and kinds — and an import line, a
|
|
30
|
+
file path, or an OpenAPI tag, path or operation ID where the answer names
|
|
31
|
+
one; other repository text stays in the MCP fence. MCP tools return structured content beside the markdown,
|
|
32
|
+
the web API the same object, and the CLI prints it with `--json`.
|
|
33
|
+
- `search --type` is lower case in every interface; the CLI no longer
|
|
34
|
+
lowercases it.
|
|
35
|
+
- A C# overload's node ID is `Type::Name#2`, `#3` and on, where it was
|
|
36
|
+
`Type::overload2_Name`, a name C# source could write.
|
|
37
|
+
- `DESCRY_SCIP_WORKERS` is removed: SCIP indexers run one at a time, and
|
|
38
|
+
the variable set nothing.
|
|
39
|
+
|
|
40
|
+
### Security
|
|
41
|
+
|
|
42
|
+
- `status` and `health` count an embeddings cache only where descry
|
|
43
|
+
signed it, as the searcher does.
|
|
44
|
+
- A model that fails to load, when the index is built or at the first
|
|
45
|
+
search, is reported by the kind of its error; its message, which may
|
|
46
|
+
name what `.descry.toml` configured, goes to the log rather than into an
|
|
47
|
+
answer.
|
|
48
|
+
- The incremental parse cache is signed: entries carry an HMAC over their
|
|
49
|
+
content, and an entry descry did not sign is ignored, never run. The key
|
|
50
|
+
is made in the user's state directory, `descry/parse.key`
|
|
51
|
+
(`XDG_STATE_HOME`, or `~/.local/state` where that is unset), and never
|
|
52
|
+
leaves it.
|
|
53
|
+
- A submodule's own config runs no program: its diff stays inline and a
|
|
54
|
+
working-tree diff starts no git inside it.
|
|
55
|
+
- `.descry.toml` values are type-checked before use: a quoted boolean, a
|
|
56
|
+
fractional depth, a string where a list belongs, a list entry that is not
|
|
57
|
+
a string or a number too large to use is ignored with a warning, where it
|
|
58
|
+
once crashed or silently took effect. A model path written as prose is
|
|
59
|
+
refused.
|
|
60
|
+
- The web's own refusals (host, origin, `/api/source`) answer the
|
|
61
|
+
contract's failure shape.
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- Incremental indexing: unchanged files are not re-parsed, and their
|
|
66
|
+
resolutions are kept with the keys of the tables they read, a node by
|
|
67
|
+
the fields read of it, so an edit that moves lines re-resolves only the
|
|
68
|
+
files that read where something is, and an MSBuild `Exists()` or
|
|
69
|
+
`GetPathOfFileAbove` condition is asked again; a cache entry of another descry
|
|
70
|
+
version, or one descry did not sign, is re-parsed. A rebuild whose
|
|
71
|
+
embeddings are all kept loads no model.
|
|
72
|
+
- `descry embedding-models --json`; with `--json`, an argument the CLI
|
|
73
|
+
cannot read answers `{error, reason}`.
|
|
74
|
+
- C#: a project's `<ImplicitUsings>` and `<Using>` items put their
|
|
75
|
+
namespaces in scope, read from its `.csproj` and the nearest
|
|
76
|
+
`Directory.Build.props` and `Directory.Build.targets` at or above it as
|
|
77
|
+
MSBuild evaluates them: every property first, the files they `<Import>`
|
|
78
|
+
(an `<ImportGroup>`'s too) in place, then the items in import order
|
|
79
|
+
(the props file's, the SDK's usings, the project's, the targets file's);
|
|
80
|
+
`;`-separated lists, nested `<Choose>`, item metadata as attributes or
|
|
81
|
+
elements, `Static` and `Alias` items as `using static` and aliases,
|
|
82
|
+
conditions of `==`, `!=`, `<`, `<=`, `>`, `>=`, `Exists()` (compared
|
|
83
|
+
on either side), string methods, `[MSBuild]::Version…` comparisons and
|
|
84
|
+
`GetPathOfFileAbove` and `GetDirectoryNameOfFileAbove` (arguments
|
|
85
|
+
quoted or a bare `$(Name)`), `$(Name)` with the reserved properties
|
|
86
|
+
(`MSBuildProjectDirectory`, `MSBuildThisFile…` and the like) and the
|
|
87
|
+
SDK-default ones (`Configuration`, `TargetFrameworkIdentifier`,
|
|
88
|
+
`RootNamespace`, `AssemblyName`); a project of several target
|
|
89
|
+
frameworks has what any of its builds has; what a `<Target>` sets is
|
|
90
|
+
left out, as it is set only when the target runs. A condition of a
|
|
91
|
+
function it does not read holds for no `Include` and every `Remove`,
|
|
92
|
+
and an `<Import>` of such a path is not followed; a file that is no XML,
|
|
93
|
+
or declares a DTD, is not read. The SDK is the `<Project Sdk>` or
|
|
94
|
+
`<Sdk Name>` one, and its list is generated from the SDK that bundles
|
|
95
|
+
the pinned runtime.
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
|
|
99
|
+
- Resolution: a local declaration shadows an import only over its block,
|
|
100
|
+
by line and column, whether a `const`, `let`, `function` or `class`, a
|
|
101
|
+
sibling block's its own (TS/JS); a method of the class or of a base
|
|
102
|
+
shadows a static import, and a nested class's inherited method comes
|
|
103
|
+
before its enclosing class's (Java); a Python import after a definition
|
|
104
|
+
rebinds it, a star import binding only what the module's `__all__`
|
|
105
|
+
lists (assigned, annotated, `+=`, `append`, `extend`; any name where it
|
|
106
|
+
is not written out, or is written in a function, deleted, or written
|
|
107
|
+
through a subscript or by name), and an awaitable is its own type until `await`,
|
|
108
|
+
`async for` or `async with` takes what it hands on (`awaited`, read from
|
|
109
|
+
typeshed's `__await__`), a project `async def`'s call a coroutine; the
|
|
110
|
+
`typing` aliases (`List`) are read from CPython's `typing.py`; a C# type
|
|
111
|
+
name is looked up namespace by namespace, a namespace's types before the
|
|
112
|
+
`using`s and aliases of the declaration of it around the code (another
|
|
113
|
+
declaration of the namespace has its own), a nested type in the class
|
|
114
|
+
and the types it derives from, a `global using` in its project's files,
|
|
115
|
+
and one no namespace or `using` reaches is not the project's, an alias
|
|
116
|
+
of the compilation unit names a path from the global namespace, one in
|
|
117
|
+
a namespace is read from there, and a dotted path reaches a type
|
|
118
|
+
through the namespaces around the code; a Java member type, own or
|
|
119
|
+
inherited (a base's `private` one is not), comes before a single-type
|
|
120
|
+
import of its name, a qualified call's too; Ruby's `::Name` is the
|
|
121
|
+
top-level constant, a constant is looked up by `Module.nesting`, then
|
|
122
|
+
the ancestors, prepended modules first, then `Object`, which a
|
|
123
|
+
`BasicObject` subclass does not have, and a `class A::B` body looks
|
|
124
|
+
names up in `A::B` and where it is written, not in `A`; C#
|
|
125
|
+
overload resolution follows the most derived type that declares an
|
|
126
|
+
applicable method (by the language's conversions and the library's
|
|
127
|
+
declared implicit conversions), a protobuf type is found as protoc finds it, Rust
|
|
128
|
+
match and `matches!` patterns are never calls while guards and
|
|
129
|
+
`select!` futures stay calls, and a call a receiver cannot reach stays
|
|
130
|
+
unresolved with its candidates rather than being dropped. A JavaScript
|
|
131
|
+
call of a name a local of a block or function around binds is no
|
|
132
|
+
import's, and a local binds no call past its block; a plain function
|
|
133
|
+
declared in a block of sloppy code is its function's variable too
|
|
134
|
+
(Annex B: not an `async` or generator one, not in a module, by its
|
|
135
|
+
syntax or its package's `"type": "module"`, a class body or under a
|
|
136
|
+
`"use strict"` prologue, nor where a parameter or a `let` of the name
|
|
137
|
+
around would clash); a named class expression's name is bound in its
|
|
138
|
+
body; a call of a class without `new`, or of a name before its `let`,
|
|
139
|
+
`const` or `class` runs, reaches nothing. A JavaScript file importing a
|
|
140
|
+
package by its name gets what Node's `exports` give it (its subpath's
|
|
141
|
+
own key, else the longest pattern; the `node`, `module-sync`, `import`
|
|
142
|
+
or `require` and `default` conditions; a `null` target, or one not
|
|
143
|
+
found as written, gives nothing), a TypeScript file `types` and its
|
|
144
|
+
tsconfig's `customConditions` too, as `require` from a CommonJS-format
|
|
145
|
+
file under node16, node18, node20 and nodenext (a dynamic `import()` as
|
|
146
|
+
`import`), without `node` under bundler resolution and without
|
|
147
|
+
`exports` under node10, the default resolution by the TypeScript
|
|
148
|
+
version where the tsconfig sets neither, and unknown where it extends
|
|
149
|
+
a config descry does not read. A C or C++ call of the one macro of its
|
|
150
|
+
name in reach, defined at the call (by the last `#define` or `#undef`
|
|
151
|
+
of the name above it, an included file read where it is included,
|
|
152
|
+
once where an include guard around every directive or a `#pragma once`
|
|
153
|
+
outside any conditional keeps it, a directive or code in an arm no
|
|
154
|
+
build compiles none: `#if 0`, `#elif 0`, `#if 0 && …`, `#if 1-1`, an
|
|
155
|
+
arm after one that always holds; a condition's integer arithmetic and
|
|
156
|
+
comparisons are evaluated as the preprocessor does, each operator at C's
|
|
157
|
+
precedence and octal, hex and binary literals read as C reads them, and one with an operator descry does
|
|
158
|
+
not read is left to the macros), is that macro, before any name is looked up, where every
|
|
159
|
+
build of the call defines it or every other definition of the name is
|
|
160
|
+
built only where it does; else the call keeps both as candidates. An
|
|
161
|
+
`#if A || B` arm is one condition, its `#else` the other. A function
|
|
162
|
+
of a macro's name has its own body; an out-of-line member is one
|
|
163
|
+
of a class of the namespace it is written in. C++ `->` reaches what a
|
|
164
|
+
type's `operator->` points at: a library template's first argument only
|
|
165
|
+
where the data's `pointers` say so, so a `vector`'s or `weak_ptr`'s no
|
|
166
|
+
longer does, and a project template's member with its arguments. A Rust
|
|
167
|
+
bare name is looked up block by block, then in its module: an item,
|
|
168
|
+
then a `use`, then what the scope's globs bring that the caller can
|
|
169
|
+
see; a path's first name is first an item a function around declares,
|
|
170
|
+
and may be a module the function or its module declares, a file one
|
|
171
|
+
(`mod m;`, `#[path]`) too; a module a function declares sees no other
|
|
172
|
+
item of that function.
|
|
173
|
+
An impl block holds a type's members where its `use` of the name leads
|
|
174
|
+
to the type, or the crate has one type of the name; another type of
|
|
175
|
+
the name lends it none. A `use` path follows every module's `use`s and
|
|
176
|
+
globs, `extern crate a as b` at a crate root, and of the files
|
|
177
|
+
`#[cfg]`s declare one module as, the writing file's own; where a
|
|
178
|
+
`#[cfg]` alternative or another crate's glob may bind the name
|
|
179
|
+
elsewhere, or the module tree leads to the module but not the item,
|
|
180
|
+
the name stays unresolved, never chosen by its file's path.
|
|
181
|
+
- `cross_lang` reads every OpenAPI operation method (`head`, `options`
|
|
182
|
+
and `trace` too), under its lower-case field name only. `evolution --crate` is the first directory of a
|
|
183
|
+
definition's path, as `search --crate` is, and a crate no definition is
|
|
184
|
+
in finds none.
|
|
185
|
+
- The C++ standard-library data reads a class's or a method's template
|
|
186
|
+
parameter as the parameter, not as an alias of its name elsewhere
|
|
187
|
+
(`basic_string::front` returns `_Traits::char_type`), and its generator
|
|
188
|
+
reads Clang's AST one declaration at a time without the parts it never
|
|
189
|
+
reads (peak memory about 1.5 GB, from over 20).
|
|
190
|
+
- C#'s implicit usings hold where the SDK's conditions do: a WPF project
|
|
191
|
+
has no `System.IO`, a Windows Forms one has `System.Windows.Forms`.
|
|
192
|
+
- `index(path)` reads the path from the project root; `status` tells the
|
|
193
|
+
SCIP state (`enabled`/`disabled`/`unavailable`/`not_loaded`) and
|
|
194
|
+
embeddings switched off as `disabled`; `changes` counts and diffs the
|
|
195
|
+
commits of its range from their merge base. `status` and `health` list
|
|
196
|
+
the languages a `lang` filter names; a `[syntax.lang_map]` name of none
|
|
197
|
+
only tags fences.
|
|
198
|
+
- What the library has comes from the generated standard-library data:
|
|
199
|
+
`_library.py`, the lists of library names the parsers skipped, and the
|
|
200
|
+
resolver's lists are gone. Read from the data instead: Ruby's
|
|
201
|
+
never-returning methods (`-> bot`) and its type makers (`Class.new`,
|
|
202
|
+
`Struct.new`, `Data.define`, from RBS; a maker whose name a project
|
|
203
|
+
constant shadows, own or a base's, makes nothing: the constant holds
|
|
204
|
+
what that constant's `new` gives, and a class that derives from
|
|
205
|
+
`BasicObject` has no Kernel methods), Dart's lower-case core types, Go's
|
|
206
|
+
GOOS and GOARCH values (`platforms`, from `internal/syslist`, a file
|
|
207
|
+
name constraining its build as go/build reads it),
|
|
208
|
+
C++'s pointer-like templates (`pointers`: those whose `->` the compiler,
|
|
209
|
+
asked, finds reaching their first type argument), and the JavaScript
|
|
210
|
+
functions that write members onto `exports` (`writers`: `Object.assign`
|
|
211
|
+
and the like, by their signatures). A base is dropped as the library's where the
|
|
212
|
+
data has it as written (a `typing` special form, `IOError`,
|
|
213
|
+
`fmt::Display` through a `use` in an inner module, a C# type of an
|
|
214
|
+
enclosing namespace, from `global::` or generic, a C++ typedef of
|
|
215
|
+
`std`, a class of a Node module). A base the language's lookup leaves
|
|
216
|
+
unknown is never taken as a project type of its name that the lookup
|
|
217
|
+
does not reach (a Java class of another package not imported, a C++
|
|
218
|
+
type of a file not compiled with the subtype's); one from another
|
|
219
|
+
package stays a reference. The data gains `qualifiers`,
|
|
220
|
+
`evaluators` (read from RBS `[self: self]`), `attributes` and
|
|
221
|
+
`context_managers` (Python's data descriptors and context-manager
|
|
222
|
+
decorators, read from typeshed), `implicit`, `implicit_usings` and
|
|
223
|
+
`platforms`; a
|
|
224
|
+
Rust method that hands on what a standard type wraps is read from the
|
|
225
|
+
type's own return types; the .NET data is read from the 10.0.14 runtime.
|
|
226
|
+
- Ruby's `puts`, `require`, `raise` and the other Kernel and Module
|
|
227
|
+
methods, Dart's `print` and Go's built-ins are calls like any other: the
|
|
228
|
+
project's own method or function of the name where scope reaches one,
|
|
229
|
+
the library's otherwise.
|
|
230
|
+
- A Rust `use` path starts at a crate unless the scope it is written in,
|
|
231
|
+
or a module a glob `use` brings, has a module or item of its first name
|
|
232
|
+
(`use util::read_all` with a `mod util`, `use Kind::Alpha` with an
|
|
233
|
+
`enum Kind`): a module named `std` (or `glob`) elsewhere in the project
|
|
234
|
+
no longer hides the crate. A glob `use` brings what the `use`s of the
|
|
235
|
+
modules it reaches bind, renamed ones and their globs too, and a `use`
|
|
236
|
+
in a body on a function's own line is the function's. A Rust base
|
|
237
|
+
is looked up by the path it is written with. A Rust bare name
|
|
238
|
+
reaches a variant or associated function only under a glob import of
|
|
239
|
+
its type; a call on a standard type that holds a project
|
|
240
|
+
type (`Option<T>`, `Arc<T>`) is answered by that type's data.
|
|
241
|
+
- `0` lifts `[timeouts] query_ms` and `[query] max_nodes`.
|
|
242
|
+
|
|
243
|
+
### Fixed
|
|
244
|
+
|
|
245
|
+
- A PHP `new class` reaches its anonymous class where the parser moved
|
|
246
|
+
lines; it named a class that did not exist.
|
|
247
|
+
- TypeScript parameter types of function types (`f: () => void`) and of
|
|
248
|
+
parameters with defaults are read; the list was split at `=>`. A
|
|
249
|
+
`for await (const x of xs)` head binds nothing, and an interceptor whose
|
|
250
|
+
callback closes on its line ends there.
|
|
251
|
+
- A Python class's decorators are calls, as a function's are.
|
|
252
|
+
- A Rust type alias whose parameter's bound returns a type
|
|
253
|
+
(`type A<F: Fn() -> u8> = B<F>;`) is read.
|
|
254
|
+
- Git failures other than "not a repository" (git missing, a timeout) are
|
|
255
|
+
reported as what they are.
|
|
256
|
+
- SCIP honours the configured `excluded_dirs` when it finds projects and
|
|
257
|
+
hashes their sources, and sees edits to a Rust project under a directory
|
|
258
|
+
named `target`.
|
|
259
|
+
- The Ruby standard library data keeps the type arguments of a type
|
|
260
|
+
written from the root (`::Array[Integer]`); the Dart generator reads a
|
|
261
|
+
libraries directory given itself.
|
|
262
|
+
- The web UI colours the flow tree's type badges.
|
|
263
|
+
- `context` finds the tests of a name with an acronym (`HTTPServer` →
|
|
264
|
+
`test_http_server`).
|
|
265
|
+
- The MCP server no longer builds embeddings for a graph indexed without
|
|
266
|
+
them, and pre-warms a graph's own ready embeddings.
|
|
267
|
+
- A deeply nested graph or embeddings sidecar, or a sidecar that is not
|
|
268
|
+
text, is reported unusable instead of crashing.
|
|
269
|
+
- A `.descry_cache` shipped as a file, or a graph shipped as a directory,
|
|
270
|
+
is refused with an error.
|
|
271
|
+
- Parser misses: one-line Java anonymous classes hold their methods, Go
|
|
272
|
+
statements on a `case` line, protobuf package-qualified names, the
|
|
273
|
+
members and lines of an unnamed C++ struct, and Rust enum variants named
|
|
274
|
+
as the prelude's (`None`, `Ok`) or written below a variant's opening
|
|
275
|
+
bracket.
|
|
276
|
+
- The Ruby data has the module functions RBS declares with `self?.`
|
|
277
|
+
(`Kernel#puts`) and the singleton aliases; the C++ data gives a method
|
|
278
|
+
the return of its overloads that return a value, written as the public
|
|
279
|
+
type the library's own typedefs name (`ostringstream::str` is
|
|
280
|
+
`basic_string`, where it was `void`).
|
|
281
|
+
- `this` inside a nested non-arrow function is the function's own; a
|
|
282
|
+
relative local embedding model has one cache key, and is loaded, from
|
|
283
|
+
the project root whatever the cwd; `context.tests` entries are node
|
|
284
|
+
IDs of test code as `[test_detection]` marks it: of the test files named
|
|
285
|
+
after the symbol's file (without the `[test_detection]` suffix or the
|
|
286
|
+
`test_` prefix that makes them tests), the `test_<name>` functions, and
|
|
287
|
+
the symbol's file's own tests that name it. `examples` and the usage examples of
|
|
288
|
+
`context` tell tests as `[test_detection]` does.
|
|
289
|
+
- `descry-web` starts when `DESCRY_LOG_LEVEL` names no level
|
|
290
|
+
(`basic_format`), at WARNING, as the CLI does.
|
|
291
|
+
- A Rust guard whose `if` ends its line or meets `(` keeps its calls, in a
|
|
292
|
+
`match` or a `matches!`; a
|
|
293
|
+
function a type has from a trait it derives (`H::default()`) gives that
|
|
294
|
+
type; a variant a glob `use` brings (`Ok` under `use Outcome::*`) is
|
|
295
|
+
that variant, before the prelude's. A Ruby call of a Kernel name in a
|
|
296
|
+
class's method, outside a block, is Kernel's where neither the class's
|
|
297
|
+
ancestry nor the project's own `Object`, `Kernel` or `BasicObject` has
|
|
298
|
+
it.
|
|
299
|
+
- `churn` counts a commit for the symbols its lines were in at that
|
|
300
|
+
commit, as `git log -L` follows a range: today's spans carried back
|
|
301
|
+
through the file's own history, each side of a merge in its own lines
|
|
302
|
+
and past one that left the file alone, across renames whatever
|
|
303
|
+
`log.follow` says, the path read as written, never as a pattern, lines
|
|
304
|
+
counted at `\n` as git counts them, a `\r` before one kept, today's file
|
|
305
|
+
matched to the last commit's without the `\r` a checkout writes (Windows'
|
|
306
|
+
`core.autocrlf`), added lines by
|
|
307
|
+
the commit's spans and removed ones by its parent's, a merge where it
|
|
308
|
+
changed a symbol against every parent. It read them against today's
|
|
309
|
+
lines, so a function added above shifted history onto its neighbours.
|
|
310
|
+
`changes` reads its range's lines against the symbols as they stood at
|
|
311
|
+
its ends. A file whose history cannot be read is counted in the
|
|
312
|
+
partial-results note.
|
|
313
|
+
- `evolution` lists the commits that changed a symbol's own lines, those no
|
|
314
|
+
definition inside it holds, followed through history as `churn` follows
|
|
315
|
+
them, so the two count alike; it read `git log -L`, which counted a nested
|
|
316
|
+
function's changes for the function around it and a merge for neither.
|
|
317
|
+
- `churn --mode files` counts every commit that changed a file, through
|
|
318
|
+
each parent of a merge, with the generated-file exclusions or without.
|
|
319
|
+
- `flow`'s tree nodes give the node type (`Function`), not its first
|
|
320
|
+
three letters. The web page's "Show callers" asks for none when
|
|
321
|
+
unticked, and an unresolved callee opens no context.
|
|
322
|
+
- With SCIP, a Python call scip-python records to a method of a package
|
|
323
|
+
the project defines nothing of (`pathlib.Path.resolve`) leaves the
|
|
324
|
+
project, unless a project class overrides that method; it is no
|
|
325
|
+
possible caller of a project method of its name.
|
|
326
|
+
- An empty `churn`, `changes` or `evolution` names what narrowed it; a
|
|
327
|
+
failed git step is named without its arguments run into the range, and
|
|
328
|
+
what git wrote is kept for the text.
|
|
329
|
+
- A JavaScript or TypeScript call of a constant that names a function
|
|
330
|
+
(`const x = helper`, `const make = Shape.create`, `const y = x`) calls
|
|
331
|
+
that function; it reached the constant.
|
|
332
|
+
- A Rust trait's path (`Ex::extra(&v)`) calls that trait's method for the
|
|
333
|
+
receiver's type, the trait's own body where the impl leaves it; it took
|
|
334
|
+
any method of the name the type had. A method call of a name two traits
|
|
335
|
+
give the type is the in-scope trait's, so it keeps both as candidates;
|
|
336
|
+
an inherent method of the name comes first. Where the type has no impl
|
|
337
|
+
of the trait, the path reaches the blanket impl it meets; the path of a
|
|
338
|
+
trait from outside (`Display::fmt(&self.inner, f)`) reaches the type's
|
|
339
|
+
impl, where it was dropped; a trait function without `self`
|
|
340
|
+
(`Build::build(1)`) keeps the impls as candidates, where it reached the
|
|
341
|
+
trait's declaration. A Rust argument dereferenced twice (`&**self`) is
|
|
342
|
+
of a type not known, as a `Deref` impl gives it; it was taken as the
|
|
343
|
+
type of what it dereferences.
|
|
344
|
+
- A Rust unit struct or unit variant written by its path (`net::Inner`,
|
|
345
|
+
`Kind::Unit`), passed, bound by `let` or called on (`m::Kind::A.tick()`),
|
|
346
|
+
is a value of its type, and so is a struct literal written after its
|
|
347
|
+
modules (`net::Big { a: 1 }`), passed or called on; a call written
|
|
348
|
+
inside another's arguments gives what it returns to the call around it.
|
|
349
|
+
Calls on them were unresolved or dropped. A `use` in a block names the
|
|
350
|
+
first name of such a path, where the path was left unknown; a glob
|
|
351
|
+
`use` there still leaves it unknown. A tuple struct or variant written
|
|
352
|
+
bare is its constructor, not a value of its type, where `let f = Tup;`
|
|
353
|
+
took it as one, and a name bound to such a constructor or to a function
|
|
354
|
+
(`let make = Kind::Held; make(1)`, `let f = net::make; f()`) calls it, as
|
|
355
|
+
does a variant called directly (`Kind::Held(1).tick()`); the call was
|
|
356
|
+
dropped and what it returned unknown. A constant by its path
|
|
357
|
+
(`Ready::ALL`, `Self::LIST`) is a value of the type it declares, `Self`
|
|
358
|
+
in it the impl's type. A call reaches no
|
|
359
|
+
struct but a tuple struct (`m::Same()` beside `struct Same { .. }` is
|
|
360
|
+
the function).
|
|
361
|
+
- A Rust method called on an element by index (`xs[0].go()`,
|
|
362
|
+
`self.items[i].go()`, `let x = &xs[i];`) is of what indexing gives: a
|
|
363
|
+
`Vec`'s, slice's or array's element, a map's value, a project type's
|
|
364
|
+
`index`; it was unresolved. A method returning `&Self::Output` returns
|
|
365
|
+
the impl's associated type, as `Self::Output` did. An index of an index
|
|
366
|
+
(`m["k"][0]`), and one bound by `let` from a project type's `index`,
|
|
367
|
+
read each in turn. A range index gives a slice, not an element, and the
|
|
368
|
+
slice's own elements by index; a range inside the index, `xs[at(0..1)]`,
|
|
369
|
+
does not make it one. An index of what a call returns
|
|
370
|
+
(`m.get(&1).unwrap()[0]`, `m.get_mut(&1)?[0]`, or `[0]` on a line of its
|
|
371
|
+
own after one) is of the call's type.
|
|
372
|
+
- A Rust tuple struct's field, `self.0`, is of the type its declaration
|
|
373
|
+
writes at that position; it had none. In a block for one instantiation,
|
|
374
|
+
`impl Named<Own>`, a type parameter is the type the block gives it.
|
|
375
|
+
- A Rust `let` with a pattern that ends with `;` before more code on its
|
|
376
|
+
line, `{ let Pair(x) = p; x.id() }`, and each later one, and a `let ..
|
|
377
|
+
else { .. };`, binds its names there; the pattern was taken for a call,
|
|
378
|
+
an edge to `Pair` nothing constructs. A `match` arm's guard,
|
|
379
|
+
`Some(x) if x.ok() =>`, binds no name: the arm's `x` read the guard's
|
|
380
|
+
as of no type, on the arm's own line as on the `match`'s. A `match`
|
|
381
|
+
whose arms are on its own line binds each arm's names, and a `let x = t`
|
|
382
|
+
of a name the line binds just before (a `for` or a closure on one line)
|
|
383
|
+
holds what the name does.
|
|
384
|
+
- What a Rust standard method returns where an argument's type decides it
|
|
385
|
+
(`v.get(0)`, `v.get(0usize)`, `v.get(i as usize)`, `v.get(0..1)`) is
|
|
386
|
+
known: the generated
|
|
387
|
+
data's new `by_argument` table holds, for each such method, what each
|
|
388
|
+
impl of the bound trait gives (`SliceIndex` for `usize` the element, for
|
|
389
|
+
a range a slice), and an integer literal without a suffix reads as the
|
|
390
|
+
integer type, a range as the language item its syntax makes (the data's
|
|
391
|
+
new `lang` table, read from rustdoc's `#[lang]`). `v.get(0).unwrap().id()`
|
|
392
|
+
was unresolved. An argument of a standard type fits a parameter of an
|
|
393
|
+
alias of it, `i32` a `RawFd` (the data's new `same`, the type each other
|
|
394
|
+
path and argument-free alias names).
|
|
395
|
+
- A Rust value is read by one model of Rust's expressions, wherever it is
|
|
396
|
+
written (a `let`, an argument, a receiver, a field, a pattern's value, a
|
|
397
|
+
closure's body), over lines too: a `match` or an `if` is what the arms
|
|
398
|
+
that do not diverge agree on (arms of different types give none), a
|
|
399
|
+
`loop` what its `break`s give (those naming its label from an inner
|
|
400
|
+
loop too), a labelled block its own and its `break`s', an `unsafe` block or a block its last expression's, behind
|
|
401
|
+
`&`, `*`, `?` or `as`. `let x = match ..` and a multi-line `if` were
|
|
402
|
+
read as of no type.
|
|
403
|
+
- A Rust value's type arguments pick between blocks for one
|
|
404
|
+
instantiation each (`impl NamedT for Named<Own>` beside `Named<Pair<T>>`),
|
|
405
|
+
as written on a parameter or in a turbofish literal, `Named::<Own> { .. }`,
|
|
406
|
+
or made by a tuple struct's call from what it is passed, `Pair(Own)`; the
|
|
407
|
+
call was unresolved with every block's method; a struct literal's type
|
|
408
|
+
arguments are those its fields' values give, `Named { t: Own }`, bound
|
|
409
|
+
by a `let` or not, a field's value a call too (`Named { t: Pair(Own) }`
|
|
410
|
+
is a `Named<Pair<Own>>`), written over lines as rustfmt writes it, with
|
|
411
|
+
a method on the line after its `}`; a struct update, `Named { ..base }`,
|
|
412
|
+
is of its base's type. A Rust block of one expression, `let u = { o };`,
|
|
413
|
+
is that expression's value. A
|
|
414
|
+
struct's fields written
|
|
415
|
+
on its own line, `struct Named<T> { pub t: T }`, have their types.
|
|
416
|
+
- A Rust closure whose block is one expression, `map(|t| { t })`, returns
|
|
417
|
+
it; what it returned was not known.
|
|
418
|
+
- A Rust block a crate writes for a type of another crate of the project
|
|
419
|
+
(`impl Wipe for Buf` after `use olib::buffer::Buffer as Buf`) holds that
|
|
420
|
+
type's methods: `self.clear()` in it and `b.wipe()` on the type were
|
|
421
|
+
dropped or unresolved.
|
|
422
|
+
- A path whose fields, indexed or not, go on below with their `.` leading
|
|
423
|
+
(`self` / `.items` / `.iter()`, `this` / `.items[0]` / `.size()`), as
|
|
424
|
+
rustfmt and prettier write long chains, keeps its receiver, and a Rust
|
|
425
|
+
`for` whose value is written so binds its pattern; the receiver was
|
|
426
|
+
lost. So does a line of `?` alone or an index alone between them, a
|
|
427
|
+
`for` whose value passes a closure with braces, and in TypeScript a
|
|
428
|
+
field with `?.` leading or after a non-null `!` (`this.deep!.leaf`), one
|
|
429
|
+
a line ends with too.
|
|
430
|
+
- A method called with its type arguments on what a call returns,
|
|
431
|
+
`registry().get<S>()`, is its type's member; it was unresolved with no
|
|
432
|
+
candidates.
|
|
433
|
+
- A TypeScript object type an exported alias writes on one line, `export
|
|
434
|
+
type Deep = { leaf: Leaf }`, has its fields, as an interface written so
|
|
435
|
+
has; a value's field of it was of no type.
|
|
436
|
+
- A value of a Rust type alias is of the type it stands for; where another
|
|
437
|
+
module declared a type of the alias's name, the alias was taken for that
|
|
438
|
+
type, and calls on its values reached that type's methods. A type and
|
|
439
|
+
an alias of one name in one module, each under its own `#[cfg]`, are
|
|
440
|
+
either, so calls on a value of the name keep both as candidates.
|
|
441
|
+
- Rust and TypeScript types of one name in one file (a struct a function
|
|
442
|
+
declares, in each of two functions) keep their own fields; each took
|
|
443
|
+
the other's, and none had a type for a field both declare. A Rust
|
|
444
|
+
`const` or `static` a function declares is seen in that function's body,
|
|
445
|
+
nested items too, where it was seen in the whole file.
|
|
446
|
+
- A Rust `impl` block is for the type the path in its header names, read
|
|
447
|
+
from the module the block is in and through its `use`s (`impl Trait for
|
|
448
|
+
crate::a::Error` beside another `Error`, `impl F` after `use
|
|
449
|
+
crate::a::Foo as F`), where only the last name was read: calls of its
|
|
450
|
+
methods reached another type's or none. A block written beside the type's module, `impl
|
|
451
|
+
T for m::Unit` beside `mod m`, files its methods under the type
|
|
452
|
+
(`m::Unit::talk`, where they were `Unit::talk`); one written in another
|
|
453
|
+
module keeps its own node, `Foo::extra`, as a block in another file
|
|
454
|
+
does.
|
|
455
|
+
- A Rust dependency renamed in its manifest, `tui = { path = "../core",
|
|
456
|
+
package = "core" }`, or taken with `workspace = true` from a renamed
|
|
457
|
+
`[workspace.dependencies]` entry, is that project crate; paths through
|
|
458
|
+
the name were read as an outside crate's and their calls dropped.
|
|
459
|
+
- A Rust block for a standard type a `use` names, `impl Wait for
|
|
460
|
+
StdChild` after `use std::process::Child as StdChild`, keeps the name
|
|
461
|
+
written. A value of a standard type the project implements a trait for
|
|
462
|
+
is of that type, not of the project's blocks for it, and a method of
|
|
463
|
+
the type's own of the name a trait gives too, which rustc picks by how
|
|
464
|
+
each takes the receiver, leaves the call unresolved with the trait's
|
|
465
|
+
as the candidate; the trait's was taken.
|
|
466
|
+
- A Rust trait's path on a type whose impl of the trait is not in the
|
|
467
|
+
source read (a macro writes it, and may override the trait's body) is
|
|
468
|
+
unresolved with the impls as candidates; it reached the trait's
|
|
469
|
+
declaration. A receiver of the trait's own type, as a bound gives it,
|
|
470
|
+
still reads the trait's function.
|
|
471
|
+
- A call resolved while another is (what a function returns, a call in
|
|
472
|
+
the arguments) leaves the other as it was; it changed whether the
|
|
473
|
+
other was written as a path and what it passed, so a Rust call could
|
|
474
|
+
keep an impl that cannot take its arguments.
|
|
475
|
+
- A JavaScript or TypeScript `#` specifier resolves through the `imports`
|
|
476
|
+
of the nearest package.json, as Node's do; it was left unresolved.
|
|
477
|
+
- A PHP method called on a property declared no type is one of the methods
|
|
478
|
+
of its name, as on an undeclared one; it was unresolved with none.
|
|
479
|
+
- C# files keep their resolution from one index to the next.
|
|
480
|
+
- An index with SCIP switched off no longer probes every indexer.
|
|
481
|
+
- `[test_detection] file_suffixes = []` (or `path_patterns = []`) is the
|
|
482
|
+
list used; it no longer falls back to a third, different list.
|
|
483
|
+
- `[embeddings] model = "./models/x"` is read as a local path.
|
|
484
|
+
- `semantic` with embeddings switched off says so, not that the
|
|
485
|
+
dependencies are missing.
|
|
486
|
+
|
|
487
|
+
## [0.3.4] — 2026-10-23
|
|
488
|
+
|
|
489
|
+
### Fixed
|
|
490
|
+
|
|
491
|
+
- `search` answers what semantic search finds when the keywords find
|
|
492
|
+
nothing: the semantic pass ran because they found little, and its
|
|
493
|
+
results were then dropped ("No matches"). It now searches under
|
|
494
|
+
`lang`, `crate`, `type` and `exclude_tests` as the keyword side does; its
|
|
495
|
+
results were merged unfiltered.
|
|
496
|
+
- `search --type` with a type the graph has none of, or one descry does
|
|
497
|
+
not know, matches nothing, as an unknown `lang` does; it was ignored and
|
|
498
|
+
every type answered. The CLI lists the types it takes.
|
|
499
|
+
- `flow --target` takes a node ID as well as a name; a node ID was never
|
|
500
|
+
found, and the trace said nothing of it.
|
|
8
501
|
|
|
9
502
|
## [0.3.3] — 2026-10-23
|
|
10
503
|
|
|
@@ -18,12 +18,12 @@ just clean # remove .descry_cache, dist, caches, __pycache__
|
|
|
18
18
|
Run a single test or filter:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
just test tests/test_handlers.py
|
|
22
|
-
just test tests/
|
|
23
|
-
just test -k "cross_lang and not slow"
|
|
21
|
+
just test tests/test_handlers.py # one file
|
|
22
|
+
just test tests/test_config.py::TestFromToml::test_from_toml_basic # one test
|
|
23
|
+
just test -k "cross_lang and not slow" # by expression
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Python 3.11+ required (enforced by `pyproject.toml`). Pre-commit gate: `just lint && just fmt && just test` all pass (
|
|
26
|
+
Python 3.11+ required (enforced by `pyproject.toml`). Pre-commit gate: `just lint && just fmt && just test` all pass (3306 tests pass; a handful more are skipped where the environment lacks their fixture — a drop is a regression signal).
|
|
27
27
|
|
|
28
28
|
Invoke the tool while developing:
|
|
29
29
|
|
|
@@ -43,14 +43,14 @@ Packaging: `pyproject.toml` defines four console scripts — `descry`, `descry-m
|
|
|
43
43
|
|
|
44
44
|
**Two graph phases:**
|
|
45
45
|
|
|
46
|
-
1. **Generate** (`generate.py`, invoked via `descry index` or `descry-generate`) — walks the project, parses source files with regex + AST, resolves calls (`CallResolver`) from imports, declared and returned types, scope and the class hierarchy, optionally runs SCIP indexers (rust-analyzer, scip-typescript) for type-aware call resolution, optionally builds embeddings. Writes `.descry_cache/codebase_graph.json`. A call is resolved, left unresolved with the definitions that fit recorded as `candidates`, or dropped as external. Never resolve by name alone when more than one definition fits, never by file path, and never guess the type of a receiver: an unknown receiver stays unresolved.
|
|
46
|
+
1. **Generate** (`generate.py`, invoked via `descry index` or `descry-generate`) — walks the project, parses source files with regex + AST (`_parsing.py` holds what every parser shares; each language has its `<lang>_parser.py`), resolves calls (`CallResolver` in `resolver.py`) from imports, declared and returned types, scope and the class hierarchy, optionally runs SCIP indexers (rust-analyzer, scip-typescript) for type-aware call resolution, optionally builds embeddings. Writes `.descry_cache/codebase_graph.json`. A call is resolved, left unresolved with the definitions that fit recorded as `candidates`, or dropped as external. Never resolve by name alone when more than one definition fits, never by file path, and never guess the type of a receiver: an unknown receiver stays unresolved.
|
|
47
47
|
2. **Query** (`query.py` → `GraphQuerier`) — loads the cached graph and answers callers/callees/flow/path/context queries. All service methods lazy-load the querier; freshness is tracked by graph file mtime.
|
|
48
48
|
|
|
49
49
|
**Graph schema is versioned.** `src/descry/_graph.py` exports `CURRENT_SCHEMA` (currently `2`) and `load_graph_with_schema()`. Every graph-consuming code path must load via this helper — it raises `GraphSchemaError` on mismatch so stale graphs are rejected rather than silently producing wrong results. If you change the graph shape in a breaking way, bump `CURRENT_SCHEMA`.
|
|
50
50
|
|
|
51
|
-
**Standard-library knowledge is generated.** `src/descry/stdlib/<lang>.json` (format in `src/descry/_stdlib.py`, loaded lazily per language) is written by `tools/stdlib/gen_<lang>.py` from the language's own machine-readable description and never edited by hand. `class StandardLibrary` in `
|
|
51
|
+
**Standard-library knowledge is generated.** `src/descry/stdlib/<lang>.json` (format in `src/descry/_stdlib.py`, loaded lazily per language) is written by `tools/stdlib/gen_<lang>.py` from the language's own machine-readable description and never edited by hand. `class StandardLibrary` in `resolver.py` is the one lookup every language plugs into, by supplying how it writes a type and how a name in source maps to the data. Do not add lists of library types, functions or members to a parser or the resolver — extend the generator. `tests/test_stdlib.py` regenerates each file when its source is present (`$DESCRY_STDLIB_SOURCES`, `$JAVA_HOME`).
|
|
52
52
|
|
|
53
|
-
**Owners are named as source names them.** A parser files members under the type that source names. A block for no type source can name (an anonymous class, a Rust `impl` for a tuple or a type parameter, an unnamed extension) gets an owner whose name no source can write (`class@12`, `object@12`, `<Trait for (A, B)>`, `extension on String`), never the name of a trait, a base or the enclosing type, so no lookup of a type lands on it. An accessor is filed as source writes it (`get size`), a name no method can have. `TestOwnersSourceCannotName` in `tests/test_generate.py` holds a fixture
|
|
53
|
+
**Owners are named as source names them.** A parser files members under the type that source names. A block for no type source can name (an anonymous class, a Rust `impl` for a tuple or a type parameter, an unnamed extension) gets an owner whose name no source can write (`class@12`, `object@12`, `<Trait for (A, B)>`, `extension on String`), never the name of a trait, a base or the enclosing type, so no lookup of a type lands on it. An accessor is filed as source writes it (`get size`), a name no method can have. `TestOwnersSourceCannotName` in `tests/test_generate.py` holds a fixture for every parser that files members under an owner source cannot name (Rust, TS/JS, Java, PHP, Dart, Ruby, C/C++); a new such parser adds one.
|
|
54
54
|
|
|
55
55
|
**Resolution changes need numbers.** Measure a resolver or parser change on public repositories before and after (`DESCRY_NO_SCIP=1 DESCRY_NO_EMBEDDINGS=1`, cache outside the repository), compare the graphs edge by edge, and read a random sample of the moved edges against the source. A wrong edge is worse than an unresolved one; a rule must be the language's, never tuned to one repository.
|
|
56
56
|
|
|
@@ -67,11 +67,12 @@ These are load-bearing — see the `### Security` sections of `CHANGELOG.md`. Do
|
|
|
67
67
|
- **The indexer child is isolated from the repository it indexes.** It is spawned with `python -P`, so a `descry/` package inside the repository cannot shadow the installed one.
|
|
68
68
|
- **Git runs with the repository's config neutralised.** `_run_git` in `git_history.py` disables hooks, fsmonitor, signature display, external diff, textconv and filter drivers, never lazily fetches (`GIT_NO_LAZY_FETCH`), forces the plain uncoloured `a/` `b/` diff format, and frames log output with NUL bytes. Arguments from callers go through the `_validate_git_*` helpers. New git calls go through `_run_git`.
|
|
69
69
|
- **Source is only read from inside the project root.** Graph node paths are untrusted (a graph file can be hand-edited or shipped by a repository); every read goes through `source_path()` in `query.py`, which refuses anything that resolves outside the root or is not a regular file.
|
|
70
|
-
- **The default cache directory stays inside the project.** A repository can ship `.descry_cache`, or its `scip` subdirectory, as a symlink to another directory; `DescryConfig.cache_dir_error()` refuses indexing and embedding there, and only `DESCRY_CACHE_DIR` names a cache outside the project.
|
|
70
|
+
- **The default cache directory stays inside the project.** A repository can ship `.descry_cache`, or its `scip` or `parse` subdirectory, as a symlink to another directory; `DescryConfig.cache_dir_error()` refuses indexing and embedding there, and only `DESCRY_CACHE_DIR` names a cache outside the project.
|
|
71
|
+
- **A parse is reused only from an entry descry wrote.** Each file's parse is kept in `<cache>/parse` as JSON, never pickle, with an HMAC of the entry's name and content under a key in the user's state directory (`$XDG_STATE_HOME/descry/parse.key` when that is absolute, else `~/.local/state`; `%LOCALAPPDATA%` on Windows); where the key would be inside the project, no entry is read or kept. An entry a repository ships, or one moved to another file's name, fails the check and the file is parsed again; restoring an entry cannot stop an index.
|
|
71
72
|
- **TOML-sourced subprocess args are validated.** `_validate_toolchain`, `_validate_scip_extra_arg`, `_validate_embedding_model` in `handlers.py` reject short flags, shell metacharacters, and out-of-root local paths. New TOML → subprocess plumbing must validate.
|
|
72
|
-
- **Web UI is local-only by design.** `descry-web` binds `127.0.0.1`, has no auth, and deliberately omits `CORSMiddleware` so browsers enforce same-origin — a tab on `evil.com` cannot read `/api/source`. State-changing requests are refused unless same-origin (`Origin` / `Sec-Fetch-Site`), so it cannot trigger `/api/index` either. The host middleware rejects non-loopback `Host` headers to defeat DNS-rebinding that would otherwise bypass the 127.0.0.1 bind. `/api/source` enforces project-root containment, rejects non-regular files
|
|
73
|
+
- **Web UI is local-only by design.** `descry-web` binds `127.0.0.1`, has no auth, and deliberately omits `CORSMiddleware` so browsers enforce same-origin — a tab on `evil.com` cannot read `/api/source`. State-changing requests are refused unless same-origin (`Origin` / `Sec-Fetch-Site`), so it cannot trigger `/api/index` either. The host middleware rejects non-loopback `Host` headers to defeat DNS-rebinding that would otherwise bypass the 127.0.0.1 bind. `/api/source` enforces project-root containment, rejects non-regular files, files over `[code_files] max_file_mb` (10 by default; 0, which a repository's `.descry.toml` can set, lifts the cap) and files with a NUL byte in their first 8 KiB, and opens every path component with `O_NOFOLLOW` (on Windows, which has no `openat`, it checks after opening that the file opened is the one the path resolves to, inside the root). `/api/index` (and `/api/index/stream`) take no path parameter (always indexes the configured root). `DescryService` runs one index at a time for every interface and stops the indexer's whole process tree when a run ends early. The page's remote scripts carry SRI hashes and every response carries the Content-Security-Policy that `web/server.py` builds from the page's own tags, allowing exactly those asset URLs; a new remote script needs an SRI hash. Don't add a `--host 0.0.0.0` flag, don't add `CORSMiddleware`, and don't expose network listeners.
|
|
73
74
|
- **MCP `index(path=...)`** is restricted to the configured project root; a path below it narrows the walk but still writes the root's graph.
|
|
74
|
-
- **Embedding cache** uses numpy safe-mode load + JSON sidecar + atomic writes
|
|
75
|
+
- **Embedding cache** uses numpy safe-mode load + JSON sidecar + atomic writes, keyed by the graph's bytes and time and the recipe. A rebuild reuses a node's vector, by the digest of its text, only from a cache descry signed with the key in the user's state directory (`signing_key()` in `_parse_cache.py`), so a shipped cache is never reused. `MODEL_REGISTRY` in `embeddings.py` is the single source of truth for which models descry ships pinned configurations for — each entry carries its revision and its task prompts. **`trust_remote_code` is always `False`**: there is no registry field, loader parameter or config key that enables it, so descry never executes code from a model repository — do not add one. A model named in `.descry.toml` that is not registered is loaded unpinned. Adding a model means adding a registry entry, not editing the loader.
|
|
75
76
|
- **The cache key is the embedding recipe's identity.** `_recipe_fingerprint()` folds in the resolved repo id, the pinned revision, `RECIPE_VERSION`, `DOCSTRING_CHAR_LIMIT`, the prompt names, the entry's loading options, and whether source code is embedded (`CODE_CHAR_LIMIT`). Anything that changes the vectors without changing the graph must be added there and `RECIPE_VERSION` bumped, or a stale cache is served against freshly-encoded queries — comparing two different embedding spaces.
|
|
76
77
|
- **`node_text()` and `code_excerpt()` are shared with the eval harness.** `tests/eval/corpus.doc_text` delegates to them so the harness's leave-one-out substitute cannot drift from the index rows it replaces. Change the text assembly there, not in a copy, and bump `RECIPE_VERSION`.
|
|
77
78
|
- **The default model has a fallback.** With no model configured, `SemanticSearcher` uses `DEFAULT_MODEL_ALIAS` and, when HuggingFace refuses it as gated, builds with `FALLBACK_MODEL_ALIAS` and warns. Searches use whichever model built the index; only a rebuild retries the default. A model named explicitly never falls back.
|
|
@@ -79,4 +80,4 @@ These are load-bearing — see the `### Security` sections of `CHANGELOG.md`. Do
|
|
|
79
80
|
|
|
80
81
|
## Versioning
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
From 1.0.0 semver holds. The only stable public API is `descry.__version__`. Submodules (`descry.handlers`, `descry.query`, etc.) are **not** stable — refactor freely. A breaking change to the library API, graph schema, CLI, or MCP tool signatures comes with a major bump; minor and patch releases don't.
|