descry-codegraph 0.3.4__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.
Files changed (225) hide show
  1. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/.github/workflows/ci.yml +7 -1
  2. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/CHANGELOG.md +480 -2
  3. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/CLAUDE.md +12 -11
  4. descry_codegraph-1.0.0/CONTRACT.md +93 -0
  5. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/PKG-INFO +64 -29
  6. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/README.md +59 -20
  7. descry_codegraph-1.0.0/SECURITY.md +66 -0
  8. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/claude-skill/README.md +1 -1
  9. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/claude-skill/descry/SKILL.md +21 -16
  10. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/claude-skill/descry/references/cli-reference.md +9 -8
  11. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/claude-skill/descry/references/mcp-tools.md +7 -7
  12. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/claude-skill/descry/references/recipes.md +9 -6
  13. descry_codegraph-1.0.0/docs/CERTAINTY.md +61 -0
  14. descry_codegraph-1.0.0/docs/TOOLS.md +659 -0
  15. descry_codegraph-1.0.0/docs/UPGRADING.md +301 -0
  16. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/justfile +9 -5
  17. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/pi-extension/descry-cli.py +8 -8
  18. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/pi-extension/descry-tools.ts +12 -12
  19. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/pyproject.toml +4 -4
  20. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/__init__.py +1 -1
  21. descry_codegraph-1.0.0/src/descry/_answers.py +7 -0
  22. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/_comments.py +6 -12
  23. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/_files.py +19 -0
  24. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/_graph.py +18 -6
  25. descry_codegraph-1.0.0/src/descry/_parse_cache.py +209 -0
  26. descry_codegraph-1.0.0/src/descry/_parsing.py +3161 -0
  27. descry_codegraph-1.0.0/src/descry/_reads.py +287 -0
  28. descry_codegraph-1.0.0/src/descry/_stdlib.py +110 -0
  29. descry_codegraph-1.0.0/src/descry/_streams.py +11 -0
  30. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/clang_parser.py +241 -80
  31. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/cli.py +199 -156
  32. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/cross_lang.py +17 -77
  33. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/dart_parser.py +27 -36
  34. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/dotnet_parser.py +176 -108
  35. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/embeddings.py +122 -87
  36. descry_codegraph-1.0.0/src/descry/generate.py +1470 -0
  37. descry_codegraph-1.0.0/src/descry/git_history.py +1517 -0
  38. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/go_parser.py +83 -73
  39. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/handlers.py +1389 -970
  40. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/java_parser.py +30 -45
  41. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/mcp_server.py +171 -165
  42. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/php_parser.py +55 -48
  43. descry_codegraph-1.0.0/src/descry/proto_parser.py +66 -0
  44. descry_codegraph-1.0.0/src/descry/python_parser.py +1030 -0
  45. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/query.py +666 -848
  46. descry_codegraph-1.0.0/src/descry/resolver.py +10074 -0
  47. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/ruby_parser.py +126 -92
  48. descry_codegraph-1.0.0/src/descry/rust_parser.py +2195 -0
  49. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapter.py +52 -38
  50. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/clang.py +6 -30
  51. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/dart.py +7 -58
  52. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/dotnet.py +15 -70
  53. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/go.py +19 -53
  54. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/java.py +18 -73
  55. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/php.py +18 -63
  56. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/python.py +27 -43
  57. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/ruby.py +5 -33
  58. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/rust.py +12 -50
  59. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/typescript.py +9 -55
  60. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/cache.py +77 -219
  61. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/parser.py +102 -194
  62. descry_codegraph-1.0.0/src/descry/scip/support.py +40 -0
  63. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/cpp.json +99 -141
  64. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/dotnet.json +652 -1
  65. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/go.json +49 -1
  66. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/python.json +60 -14
  67. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/ruby.json +649 -414
  68. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/rust.json +2714 -0
  69. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/typescript.json +6 -1
  70. descry_codegraph-1.0.0/src/descry/ts_parser.py +6089 -0
  71. descry_codegraph-1.0.0/src/descry/web/server.py +803 -0
  72. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/web/web/index.html +188 -282
  73. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/web/web/style.css +1 -70
  74. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/conftest.py +12 -0
  75. descry_codegraph-1.0.0/tests/ground_truth/c/Makefile +2 -0
  76. descry_codegraph-1.0.0/tests/ground_truth/c/expected.json +14 -0
  77. descry_codegraph-1.0.0/tests/ground_truth/c/io.c +7 -0
  78. descry_codegraph-1.0.0/tests/ground_truth/c/io.h +2 -0
  79. descry_codegraph-1.0.0/tests/ground_truth/c/main.c +3 -0
  80. descry_codegraph-1.0.0/tests/ground_truth/c/order.c +5 -0
  81. descry_codegraph-1.0.0/tests/ground_truth/cpp/Makefile +2 -0
  82. descry_codegraph-1.0.0/tests/ground_truth/cpp/expected.json +17 -0
  83. descry_codegraph-1.0.0/tests/ground_truth/cpp/main.cpp +9 -0
  84. descry_codegraph-1.0.0/tests/ground_truth/cpp/models.hpp +25 -0
  85. descry_codegraph-1.0.0/tests/ground_truth/cpp/reader.hpp +9 -0
  86. descry_codegraph-1.0.0/tests/ground_truth/csharp/Gt.csproj +6 -0
  87. descry_codegraph-1.0.0/tests/ground_truth/csharp/Io.cs +15 -0
  88. descry_codegraph-1.0.0/tests/ground_truth/csharp/Models.cs +36 -0
  89. descry_codegraph-1.0.0/tests/ground_truth/csharp/Program.cs +9 -0
  90. descry_codegraph-1.0.0/tests/ground_truth/csharp/expected.json +17 -0
  91. descry_codegraph-1.0.0/tests/ground_truth/dart/bin/main.dart +8 -0
  92. descry_codegraph-1.0.0/tests/ground_truth/dart/expected.json +78 -0
  93. descry_codegraph-1.0.0/tests/ground_truth/dart/lib/io.dart +9 -0
  94. descry_codegraph-1.0.0/tests/ground_truth/dart/lib/models.dart +21 -0
  95. descry_codegraph-1.0.0/tests/ground_truth/dart/pubspec.yaml +3 -0
  96. descry_codegraph-1.0.0/tests/ground_truth/go/expected.json +17 -0
  97. descry_codegraph-1.0.0/tests/ground_truth/go/go.mod +3 -0
  98. descry_codegraph-1.0.0/tests/ground_truth/go/io.go +13 -0
  99. descry_codegraph-1.0.0/tests/ground_truth/go/main.go +7 -0
  100. descry_codegraph-1.0.0/tests/ground_truth/go/models.go +20 -0
  101. descry_codegraph-1.0.0/tests/ground_truth/java/expected.json +19 -0
  102. descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Base.java +11 -0
  103. descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Io.java +7 -0
  104. descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Main.java +9 -0
  105. descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Order.java +7 -0
  106. descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/Reader.java +7 -0
  107. descry_codegraph-1.0.0/tests/ground_truth/java/src/gt/User.java +15 -0
  108. descry_codegraph-1.0.0/tests/ground_truth/javascript/expected.json +91 -0
  109. descry_codegraph-1.0.0/tests/ground_truth/javascript/package.json +1 -0
  110. descry_codegraph-1.0.0/tests/ground_truth/javascript/src/io.js +14 -0
  111. descry_codegraph-1.0.0/tests/ground_truth/javascript/src/main.js +13 -0
  112. descry_codegraph-1.0.0/tests/ground_truth/javascript/src/models.js +28 -0
  113. descry_codegraph-1.0.0/tests/ground_truth/php/composer.json +1 -0
  114. descry_codegraph-1.0.0/tests/ground_truth/php/expected.json +86 -0
  115. descry_codegraph-1.0.0/tests/ground_truth/php/src/Io.php +19 -0
  116. descry_codegraph-1.0.0/tests/ground_truth/php/src/Models.php +38 -0
  117. descry_codegraph-1.0.0/tests/ground_truth/php/src/main.php +17 -0
  118. descry_codegraph-1.0.0/tests/ground_truth/python/app/__init__.py +0 -0
  119. descry_codegraph-1.0.0/tests/ground_truth/python/app/io.py +14 -0
  120. descry_codegraph-1.0.0/tests/ground_truth/python/app/main.py +13 -0
  121. descry_codegraph-1.0.0/tests/ground_truth/python/app/models.py +20 -0
  122. descry_codegraph-1.0.0/tests/ground_truth/python/expected.json +90 -0
  123. descry_codegraph-1.0.0/tests/ground_truth/python/pyproject.toml +2 -0
  124. descry_codegraph-1.0.0/tests/ground_truth/ruby/Gemfile +1 -0
  125. descry_codegraph-1.0.0/tests/ground_truth/ruby/expected.json +85 -0
  126. descry_codegraph-1.0.0/tests/ground_truth/ruby/lib/io.rb +16 -0
  127. descry_codegraph-1.0.0/tests/ground_truth/ruby/lib/main.rb +13 -0
  128. descry_codegraph-1.0.0/tests/ground_truth/ruby/lib/models.rb +26 -0
  129. descry_codegraph-1.0.0/tests/ground_truth/rust/Cargo.toml +4 -0
  130. descry_codegraph-1.0.0/tests/ground_truth/rust/expected.json +77 -0
  131. descry_codegraph-1.0.0/tests/ground_truth/rust/src/io.rs +11 -0
  132. descry_codegraph-1.0.0/tests/ground_truth/rust/src/main.rs +11 -0
  133. descry_codegraph-1.0.0/tests/ground_truth/rust/src/models.rs +42 -0
  134. descry_codegraph-1.0.0/tests/ground_truth/typescript/expected.json +87 -0
  135. descry_codegraph-1.0.0/tests/ground_truth/typescript/package.json +1 -0
  136. descry_codegraph-1.0.0/tests/ground_truth/typescript/src/io.ts +13 -0
  137. descry_codegraph-1.0.0/tests/ground_truth/typescript/src/main.ts +14 -0
  138. descry_codegraph-1.0.0/tests/ground_truth/typescript/src/models.ts +28 -0
  139. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_backtracking.py +19 -12
  140. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_clang_parser.py +1 -2
  141. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_cli.py +63 -46
  142. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_config.py +63 -17
  143. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_cross_lang.py +63 -33
  144. descry_codegraph-1.0.0/tests/test_docs.py +22 -0
  145. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_dotnet_parser.py +7 -7
  146. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_embeddings.py +94 -8
  147. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_generate.py +5336 -282
  148. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_git_history.py +511 -122
  149. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_go_parser.py +16 -30
  150. descry_codegraph-1.0.0/tests/test_ground_truth.py +171 -0
  151. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_handlers.py +502 -111
  152. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_hardening.py +151 -26
  153. descry_codegraph-1.0.0/tests/test_interfaces.py +581 -0
  154. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_mcp.py +99 -13
  155. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_non_regular_files.py +2 -2
  156. descry_codegraph-1.0.0/tests/test_parse_cache.py +227 -0
  157. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_query.py +383 -379
  158. descry_codegraph-1.0.0/tests/test_resolve_cache.py +233 -0
  159. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_ruby_parser.py +1 -0
  160. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip.py +136 -59
  161. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_cache.py +120 -32
  162. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_clang.py +10 -11
  163. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_dart.py +7 -9
  164. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_dotnet.py +3 -12
  165. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_go.py +1 -8
  166. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_java.py +16 -25
  167. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_php.py +1 -10
  168. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_scip_ruby.py +1 -10
  169. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_stdlib.py +126 -7
  170. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_web_server.py +264 -263
  171. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_written_through.py +5 -5
  172. descry_codegraph-1.0.0/tools/docs/gen_tools.py +167 -0
  173. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/README.md +58 -22
  174. descry_codegraph-1.0.0/tools/stdlib/common.py +171 -0
  175. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_c.py +31 -58
  176. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_cpp.py +188 -125
  177. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_dart.py +25 -33
  178. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_dotnet.py +113 -32
  179. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_go.py +27 -20
  180. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_java.py +8 -1
  181. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_node.py +2 -2
  182. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_php.py +6 -1
  183. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_python.py +175 -50
  184. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_ruby.py +89 -60
  185. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_rust.py +208 -3
  186. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tools/stdlib/gen_typescript.py +48 -33
  187. descry_codegraph-0.3.4/SECURITY.md +0 -27
  188. descry_codegraph-0.3.4/src/descry/_stdlib.py +0 -71
  189. descry_codegraph-0.3.4/src/descry/generate.py +0 -25636
  190. descry_codegraph-0.3.4/src/descry/git_history.py +0 -1771
  191. descry_codegraph-0.3.4/src/descry/scip/support.py +0 -70
  192. descry_codegraph-0.3.4/src/descry/web/server.py +0 -1089
  193. descry_codegraph-0.3.4/tests/test_interfaces.py +0 -296
  194. descry_codegraph-0.3.4/tools/stdlib/common.py +0 -76
  195. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/.github/assets/01-landing.png +0 -0
  196. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/.github/workflows/release.yml +0 -0
  197. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/.gitignore +0 -0
  198. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/LICENSE +0 -0
  199. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/THIRD-PARTY-NOTICES.md +0 -0
  200. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/pi-extension/README.md +0 -0
  201. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/_env.py +0 -0
  202. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/__init__.py +0 -0
  203. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/__init__.py +0 -0
  204. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/_empty_environment.json +0 -0
  205. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/adapters/_java_compat.init.gradle +0 -0
  206. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/scip/pb2.py +0 -0
  207. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/c.json +0 -0
  208. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/dart.json +0 -0
  209. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/java.json +0 -0
  210. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/node.json +0 -0
  211. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/stdlib/php.json +0 -0
  212. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/src/descry/web/__init__.py +0 -0
  213. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/__init__.py +0 -0
  214. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/README.md +0 -0
  215. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/__init__.py +0 -0
  216. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/compare.py +0 -0
  217. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/corpus.py +0 -0
  218. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/metrics.py +0 -0
  219. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/registry.py +0 -0
  220. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/results/results-descry.json +0 -0
  221. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/eval/results/results-ripgrep.json +0 -0
  222. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/fixtures/small_graph.json +0 -0
  223. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_dart_parser.py +0 -0
  224. {descry_codegraph-0.3.4 → descry_codegraph-1.0.0}/tests/test_java_parser.py +0 -0
  225. {descry_codegraph-0.3.4 → 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: ubuntu-latest
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,487 @@
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 pre-1.0 semver (minor-version bumps may include breaking changes; see README versioning stance).
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
- ## [Unreleased]
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.
8
486
 
9
487
  ## [0.3.4] — 2026-10-23
10
488
 
@@ -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 # one file
22
- just test tests/test_handlers.py::test_index # one test
23
- just test -k "cross_lang and not slow" # by expression
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 (2822 tests currently — a drop is a regression signal).
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 `generate.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`).
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 per language; a new parser adds one.
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 / non-text content / >10 MiB, and opens every path component with `O_NOFOLLOW`. `/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
+ - **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 + content-addressed keys. `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
+ - **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
- Pre-1.0. The only stable public API is `descry.__version__`. Submodules (`descry.handlers`, `descry.query`, etc.) are **not** stable — refactor freely. Minor bumps (`0.1.x` → `0.2.x`) may break the library API, graph schema, CLI, or MCP tool signatures; patch bumps won't.
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.