sourcecode 1.30.25__tar.gz → 1.30.27__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 (196) hide show
  1. {sourcecode-1.30.25 → sourcecode-1.30.27}/PKG-INFO +3 -3
  2. {sourcecode-1.30.25 → sourcecode-1.30.27}/README.md +2 -2
  3. {sourcecode-1.30.25 → sourcecode-1.30.27}/pyproject.toml +1 -1
  4. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/confidence_analyzer.py +10 -2
  6. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/file_classifier.py +11 -10
  7. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/prepare_context.py +4 -0
  8. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/ranking_engine.py +92 -0
  9. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/serializer.py +201 -58
  10. sourcecode-1.30.27/tests/test_scoring_calibration.py +561 -0
  11. {sourcecode-1.30.25 → sourcecode-1.30.27}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  12. {sourcecode-1.30.25 → sourcecode-1.30.27}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  13. {sourcecode-1.30.25 → sourcecode-1.30.27}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  14. {sourcecode-1.30.25 → sourcecode-1.30.27}/.continue-here.md +0 -0
  15. {sourcecode-1.30.25 → sourcecode-1.30.27}/.github/workflows/build-windows.yml +0 -0
  16. {sourcecode-1.30.25 → sourcecode-1.30.27}/.gitignore +0 -0
  17. {sourcecode-1.30.25 → sourcecode-1.30.27}/.ruff.toml +0 -0
  18. {sourcecode-1.30.25 → sourcecode-1.30.27}/CONTRIBUTING.md +0 -0
  19. {sourcecode-1.30.25 → sourcecode-1.30.27}/LICENSE +0 -0
  20. {sourcecode-1.30.25 → sourcecode-1.30.27}/SECURITY.md +0 -0
  21. {sourcecode-1.30.25 → sourcecode-1.30.27}/docs/privacy.md +0 -0
  22. {sourcecode-1.30.25 → sourcecode-1.30.27}/docs/schema.md +0 -0
  23. {sourcecode-1.30.25 → sourcecode-1.30.27}/raw +0 -0
  24. {sourcecode-1.30.25 → sourcecode-1.30.27}/run_cli.py +0 -0
  25. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/adaptive_scanner.py +0 -0
  26. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/architecture_analyzer.py +0 -0
  27. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/architecture_summary.py +0 -0
  28. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/ast_extractor.py +0 -0
  29. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/classifier.py +0 -0
  30. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/cli.py +0 -0
  31. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/code_notes_analyzer.py +0 -0
  32. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/context_scorer.py +0 -0
  33. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/context_summarizer.py +0 -0
  34. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/contract_model.py +0 -0
  35. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/contract_pipeline.py +0 -0
  36. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/coverage_parser.py +0 -0
  37. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/dependency_analyzer.py +0 -0
  38. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/__init__.py +0 -0
  39. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/base.py +0 -0
  40. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/csproj_parser.py +0 -0
  41. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/dart.py +0 -0
  42. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/dotnet.py +0 -0
  43. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/elixir.py +0 -0
  44. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/go.py +0 -0
  45. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/heuristic.py +0 -0
  46. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/hybrid.py +0 -0
  47. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/java.py +0 -0
  48. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/jvm_ext.py +0 -0
  49. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/nodejs.py +0 -0
  50. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/parsers.py +0 -0
  51. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/php.py +0 -0
  52. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/project.py +0 -0
  53. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/python.py +0 -0
  54. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/ruby.py +0 -0
  55. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/rust.py +0 -0
  56. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/systems.py +0 -0
  57. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/terraform.py +0 -0
  58. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/detectors/tooling.py +0 -0
  59. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/doc_analyzer.py +0 -0
  60. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/entrypoint_classifier.py +0 -0
  61. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/env_analyzer.py +0 -0
  62. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/flow_analyzer.py +0 -0
  63. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/git_analyzer.py +0 -0
  64. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/graph_analyzer.py +0 -0
  65. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/metrics_analyzer.py +0 -0
  66. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/pr_comment_renderer.py +0 -0
  67. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/progress.py +0 -0
  68. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/redactor.py +0 -0
  69. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/relevance_scorer.py +0 -0
  70. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/repo_classifier.py +0 -0
  71. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/repository_ir.py +0 -0
  72. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/runtime_classifier.py +0 -0
  73. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/scanner.py +0 -0
  74. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/schema.py +0 -0
  75. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/semantic_analyzer.py +0 -0
  76. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/summarizer.py +0 -0
  77. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/telemetry/__init__.py +0 -0
  78. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/telemetry/config.py +0 -0
  79. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/telemetry/consent.py +0 -0
  80. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/telemetry/events.py +0 -0
  81. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/telemetry/filters.py +0 -0
  82. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/telemetry/transport.py +0 -0
  83. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/tree_utils.py +0 -0
  84. {sourcecode-1.30.25 → sourcecode-1.30.27}/src/sourcecode/workspace.py +0 -0
  85. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/__init__.py +0 -0
  86. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/conftest.py +0 -0
  87. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/coverage.xml +0 -0
  88. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  89. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/fastapi_app/src/main.py +0 -0
  90. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  91. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/go_service/go.mod +0 -0
  92. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/jacoco.xml +0 -0
  93. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/latin1_sample.java +0 -0
  94. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/latin1_sample_iso.java +0 -0
  95. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/lcov.info +0 -0
  96. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  97. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/nextjs_app/package.json +0 -0
  98. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  99. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  100. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  101. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  102. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  103. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  104. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  105. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  106. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  107. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  108. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  109. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  110. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  111. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  112. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  113. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  114. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  115. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  116. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  117. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  118. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  119. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  120. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  121. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  122. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  123. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  124. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  125. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  126. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  127. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  128. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  129. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  130. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  131. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_architecture_analyzer.py +0 -0
  132. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_architecture_summary.py +0 -0
  133. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_ast_extractor.py +0 -0
  134. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_block1_reliability.py +0 -0
  135. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_block2_coverage.py +0 -0
  136. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_block5_quality.py +0 -0
  137. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_bug_fixes_v16.py +0 -0
  138. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_classifier.py +0 -0
  139. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_cli.py +0 -0
  140. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_code_notes_analyzer.py +0 -0
  141. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_context_scorer.py +0 -0
  142. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_contract_pipeline.py +0 -0
  143. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_coverage_parser.py +0 -0
  144. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_cross_consistency.py +0 -0
  145. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_dependency_analyzer_node_python.py +0 -0
  146. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_dependency_analyzer_polyglot.py +0 -0
  147. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_dependency_schema.py +0 -0
  148. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_dotnet.py +0 -0
  149. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_go_rust_java.py +0 -0
  150. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_nodejs.py +0 -0
  151. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_php_ruby_dart.py +0 -0
  152. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_python.py +0 -0
  153. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_universal_managed.py +0 -0
  154. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detector_universal_systems.py +0 -0
  155. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_detectors_base.py +0 -0
  156. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_doc_analyzer_jsdom.py +0 -0
  157. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_doc_analyzer_python.py +0 -0
  158. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_encoding_regression.py +0 -0
  159. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_graph_analyzer_polyglot.py +0 -0
  160. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_graph_analyzer_python_node.py +0 -0
  161. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_graph_schema.py +0 -0
  162. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_hybrid_inference.py +0 -0
  163. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration.py +0 -0
  164. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_dependencies.py +0 -0
  165. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_detection.py +0 -0
  166. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_docs.py +0 -0
  167. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_graph_modules.py +0 -0
  168. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_lqn.py +0 -0
  169. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_metrics.py +0 -0
  170. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_multistack.py +0 -0
  171. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_semantics.py +0 -0
  172. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_integration_universal.py +0 -0
  173. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_java_spring_integration.py +0 -0
  174. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_metrics_analyzer.py +0 -0
  175. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_output_ux.py +0 -0
  176. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_packaging.py +0 -0
  177. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_phase1_improvements.py +0 -0
  178. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_pipeline_integrity.py +0 -0
  179. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_real_projects.py +0 -0
  180. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_redactor.py +0 -0
  181. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_repository_ir.py +0 -0
  182. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_scanner.py +0 -0
  183. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_schema.py +0 -0
  184. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_schema_normalization.py +0 -0
  185. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_semantic_analyzer_node.py +0 -0
  186. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_semantic_analyzer_python.py +0 -0
  187. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_semantic_import_resolution.py +0 -0
  188. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_semantic_schema.py +0 -0
  189. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_signal_hierarchy.py +0 -0
  190. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_summarizer.py +0 -0
  191. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_surface_honesty.py +0 -0
  192. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_task_differentiation.py +0 -0
  193. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_telemetry.py +0 -0
  194. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_v131_improvements.py +0 -0
  195. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_v1_10_regressions.py +0 -0
  196. {sourcecode-1.30.25 → sourcecode-1.30.27}/tests/test_workspace_analyzer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.30.25
3
+ Version: 1.30.27
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -221,7 +221,7 @@ Description-Content-Type: text/markdown
221
221
 
222
222
  **Deterministic, behavior-aware codebase context for AI agents and PR review.**
223
223
 
224
- ![Version](https://img.shields.io/badge/version-1.30.25-blue)
224
+ ![Version](https://img.shields.io/badge/version-1.30.27-blue)
225
225
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
226
226
 
227
227
  ---
@@ -257,7 +257,7 @@ pipx install sourcecode
257
257
 
258
258
  ```bash
259
259
  sourcecode version
260
- # sourcecode 1.30.25
260
+ # sourcecode 1.30.27
261
261
  ```
262
262
 
263
263
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Deterministic, behavior-aware codebase context for AI agents and PR review.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.30.25-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.30.27-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
7
7
 
8
8
  ---
@@ -38,7 +38,7 @@ pipx install sourcecode
38
38
 
39
39
  ```bash
40
40
  sourcecode version
41
- # sourcecode 1.30.25
41
+ # sourcecode 1.30.27
42
42
  ```
43
43
 
44
44
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.30.25"
7
+ version = "1.30.27"
8
8
  description = "Deterministic codebase context for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.30.25"
3
+ __version__ = "1.30.27"
@@ -290,10 +290,18 @@ class ConfidenceAnalyzer:
290
290
  elif production_eps and all(ep.runtime_relevance == "low" for ep in production_eps):
291
291
  overall = _min_confidence([overall, "low"])
292
292
 
293
- # Factor in architecture confidence when available
293
+ # Factor in architecture confidence when available.
294
+ # Key rule: if a pattern was detected (not None/"unknown"), arch.confidence="low"
295
+ # typically reflects missing documentation (no OpenAPI/ADR), not structural uncertainty.
296
+ # In that case, clamp the downgrade to "medium" so that high stack + high entry_points
297
+ # is not contradicted by a docs gap.
294
298
  arch = sm.architecture
295
299
  if arch is not None and arch.requested:
296
- overall = _min_confidence([overall, arch.confidence])
300
+ arch_conf_for_overall = arch.confidence
301
+ if arch.confidence == "low" and arch.pattern not in (None, "unknown"):
302
+ # Pattern was detected — low is docs-only, not structural; cap downgrade at medium
303
+ arch_conf_for_overall = "medium"
304
+ overall = _min_confidence([overall, arch_conf_for_overall])
297
305
  if arch.pattern in (None, "unknown"):
298
306
  # Architecture could not be inferred — don't let stack alone push to high
299
307
  if overall == "high":
@@ -84,16 +84,17 @@ _JAVA_ANNOTATION_RE = re.compile(r'@(RestController|Controller|Service|Repositor
84
84
  # (annotation_set, category, relevance, why_template)
85
85
  # Checked in priority order; first match wins.
86
86
  _JAVA_STEREOTYPE_RULES: list[tuple[frozenset, str, float, str]] = [
87
- (frozenset({"EnableWebSecurity"}), "security", 0.85, "Spring Security configuration"),
88
- (frozenset({"RestController"}), "api_endpoint", 0.90, "Spring REST controller — defines HTTP API surface"),
89
- (frozenset({"Controller", "RequestMapping"}), "api_endpoint", 0.80, "Spring MVC controller"),
90
- (frozenset({"Service", "Transactional"}), "business_logic", 0.75, "Transactional servicebusiness logic boundary"),
91
- (frozenset({"Service"}), "business_logic", 0.65, "Spring service component"),
92
- (frozenset({"Repository"}), "data_access", 0.65, "Spring repository — data access layer"),
93
- (frozenset({"Mapper"}), "data_access", 0.65, "MyBatis mapperSQL data access"),
94
- (frozenset({"Configuration"}), "configuration", 0.70, "Spring configuration class"),
95
- (frozenset({"Entity"}), "domain_model", 0.50, "JPA entity — domain model"),
96
- (frozenset({"Data"}), "dto", 0.40, "Lombok DTO"),
87
+ (frozenset({"EnableWebSecurity"}), "security", 0.85, "Spring Security configuration"),
88
+ (frozenset({"RestController"}), "api_endpoint", 0.90, "Spring REST controller — defines HTTP API surface"),
89
+ (frozenset({"Controller", "RequestMapping"}), "api_endpoint", 0.80, "Spring MVC controller"),
90
+ (frozenset({"ControllerAdvice"}), "exception_handler", 0.75, "Spring @ControllerAdvicecross-cutting exception handling"),
91
+ (frozenset({"Service", "Transactional"}), "business_logic", 0.75, "Transactional service — business logic boundary"),
92
+ (frozenset({"Service"}), "business_logic", 0.65, "Spring service component"),
93
+ (frozenset({"Repository"}), "data_access", 0.65, "Spring repository — data access layer"),
94
+ (frozenset({"Mapper"}), "data_access", 0.65, "MyBatis mapper — SQL data access"),
95
+ (frozenset({"Configuration"}), "configuration", 0.70, "Spring configuration class"),
96
+ (frozenset({"Entity"}), "domain_model", 0.50, "JPA entity — domain model"),
97
+ (frozenset({"Data"}), "dto", 0.40, "Lombok DTO"),
97
98
  ]
98
99
 
99
100
  # Categories produced by Java stereotype detection — used downstream to apply direct relevance
@@ -2596,6 +2596,10 @@ class TaskContextBuilder:
2596
2596
  # These are INFERRED (LOW CONFIDENCE) — stem match, not annotation evidence.
2597
2597
  if any(kw in stem_lower for kw in ("validator", "validation")):
2598
2598
  return "validation_component"
2599
+ if any(kw in stem_lower for kw in ("filter", "interceptor", "aspect")):
2600
+ return "runtime_filter"
2601
+ if any(kw in stem_lower for kw in ("advice", "advise", "exceptionhandler", "errorhandler")):
2602
+ return "exception_handler"
2599
2603
  if any(kw in stem_lower for kw in ("controller", "resource", "endpoint", "rest")):
2600
2604
  return "external_interface"
2601
2605
  if any(kw in stem_lower for kw in ("service", "svc", "usecase", "facade")):
@@ -251,3 +251,95 @@ class RankingEngine:
251
251
 
252
252
  def is_auxiliary(self, path: str) -> bool:
253
253
  return self._scorer.is_auxiliary(path)
254
+
255
+
256
+ # ---------------------------------------------------------------------------
257
+ # Mandatory scoring formula — deterministic 5-component impact model
258
+ # ---------------------------------------------------------------------------
259
+
260
+ # runtime_impact: execution-path role of the file
261
+ _RUNTIME_IMPACT: dict[str | None, float] = {
262
+ "api_endpoint": 1.0, # @RestController / @Controller
263
+ "security": 1.0, # @EnableWebSecurity / security filters
264
+ "runtime_core": 1.0, # confirmed production entrypoint
265
+ "cli_entrypoint": 1.0,
266
+ "exception_handler": 0.8, # @ControllerAdvice
267
+ "business_logic": 0.7, # @Service (with or without @Transactional)
268
+ "api_layer": 0.7, # API framework import (non-annotation evidence)
269
+ "data_access": 0.5, # @Repository / @Mapper
270
+ "database_layer": 0.5, # DB framework import
271
+ "infrastructure": 0.5, # infra dependency import
272
+ "configuration": 0.4, # @Configuration
273
+ "application_logic": 0.3, # code defs + imports, no framework annotation
274
+ "domain_model": 0.3, # @Entity / domain models
275
+ "dto": 0.2, # @Data / pure data carriers
276
+ "build_system": 0.15,
277
+ "tests": 0.1,
278
+ "tooling": 0.05,
279
+ None: 0.15, # unclassified source file
280
+ }
281
+
282
+ # framework_signal_strength: annotation / import evidence quality
283
+ # Spring annotation → 0.3; security component → +0.2 (total 0.5); import only → 0.2
284
+ _FRAMEWORK_SIGNAL: dict[str | None, float] = {
285
+ "security": 0.5, # Spring Security annotation + security component
286
+ "api_endpoint": 0.3, # @RestController / @Controller
287
+ "exception_handler": 0.3, # @ControllerAdvice
288
+ "business_logic": 0.3, # @Service
289
+ "data_access": 0.3, # @Repository / @Mapper
290
+ "configuration": 0.3, # @Configuration
291
+ "domain_model": 0.3, # JPA @Entity
292
+ "dto": 0.3, # Lombok @Data
293
+ "api_layer": 0.2, # framework import (weaker than annotation)
294
+ "database_layer": 0.2,
295
+ "infrastructure": 0.2,
296
+ }
297
+
298
+ # Normalization ceiling per evidence tier — used when spread < 0.40
299
+ _NORM_TARGET_HI: dict[str, float] = {
300
+ "api_endpoint": 0.90,
301
+ "security": 0.90,
302
+ "exception_handler": 0.82,
303
+ "business_logic": 0.80,
304
+ "api_layer": 0.80,
305
+ "data_access": 0.70,
306
+ "database_layer": 0.70,
307
+ "infrastructure": 0.70,
308
+ "configuration": 0.65,
309
+ }
310
+
311
+
312
+ def resolve_runtime_impact(category: str | None) -> float:
313
+ """Map FileClassifier category → runtime_impact [0.0, 1.0]."""
314
+ return _RUNTIME_IMPACT.get(category, _RUNTIME_IMPACT[None])
315
+
316
+
317
+ def resolve_framework_signal(category: str | None) -> float:
318
+ """Map FileClassifier category → framework_signal_strength [0.0, 0.5]."""
319
+ return _FRAMEWORK_SIGNAL.get(category, 0.0)
320
+
321
+
322
+ def compute_impact_score(
323
+ runtime_impact: float,
324
+ dependency_centrality: float,
325
+ framework_signal_strength: float,
326
+ change_type_severity: float,
327
+ test_risk_factor: float,
328
+ ) -> float:
329
+ """Mandatory weighted scoring formula.
330
+
331
+ score = 0.35×runtime_impact + 0.25×dependency_centrality
332
+ + 0.20×framework_signal_strength + 0.10×change_type_severity
333
+ + 0.10×test_risk_factor
334
+
335
+ All inputs [0.0, 1.0]. Output clamped [0.0, 1.0].
336
+ Deterministic: same inputs always produce same output.
337
+ """
338
+ raw = (
339
+ 0.35 * runtime_impact
340
+ + 0.25 * dependency_centrality
341
+ + 0.20 * framework_signal_strength
342
+ + 0.10 * change_type_severity
343
+ + 0.10 * test_risk_factor
344
+ )
345
+ return max(0.0, min(1.0, raw))
@@ -660,6 +660,116 @@ def _jndi_datasources(sm: "SourceMap") -> "Optional[list[dict[str, Any]]]":
660
660
  return datasources
661
661
 
662
662
 
663
+ def _tiered_display_score(
664
+ pre_bonus_combined: float,
665
+ file_class: Any,
666
+ path: str,
667
+ entry_paths: set,
668
+ has_structural_signals: bool = False,
669
+ ) -> float:
670
+ """Evidence-tiered display score [0.0, 1.0].
671
+
672
+ Tiers enforce: strong evidence > medium evidence > filesystem/path only.
673
+ M3 sort bonuses must NOT be included in pre_bonus_combined — they are for
674
+ ordering only and must not inflate the displayed score.
675
+
676
+ Tier ceilings:
677
+ T1 confirmed production entrypoint 0.92–1.00
678
+ T2 entrypoint (weaker category) 0.80–0.91
679
+ T3 annotation-confirmed stereotype 0.40–0.90 (table-calibrated)
680
+ T4 framework import evidence 0.55–0.79
681
+ T5 code definitions + imports 0.38–0.54
682
+ T6 build manifest / tooling / test 0.25–0.45
683
+ T7 path/filesystem signal only 0.10–0.39
684
+ """
685
+ from sourcecode.file_classifier import JAVA_STEREOTYPE_CATEGORIES
686
+
687
+ cat = file_class.category if file_class else None
688
+ base_rel = file_class.relevance if file_class else 0.0
689
+
690
+ # T1: confirmed production entrypoint
691
+ if path in entry_paths and cat in ("runtime_core", "cli_entrypoint"):
692
+ return round(min(1.0, max(0.92, base_rel)), 3)
693
+
694
+ # T2: in entry_paths but weaker evidence category
695
+ if path in entry_paths:
696
+ return round(min(0.91, max(0.80, pre_bonus_combined / 2.0)), 3)
697
+
698
+ # T3: annotation-confirmed stereotype — table values are already calibrated
699
+ if file_class and cat in JAVA_STEREOTYPE_CATEGORIES:
700
+ return round(base_rel, 3)
701
+
702
+ # T4: framework import evidence (medium strength)
703
+ if cat in ("api_layer", "database_layer", "infrastructure"):
704
+ return round(min(0.79, max(0.55, pre_bonus_combined / 2.0)), 3)
705
+
706
+ # T5: code definitions with imports (medium-low)
707
+ if cat in ("application_logic", "domain_model"):
708
+ return round(min(0.54, max(0.38, pre_bonus_combined / 2.0)), 3)
709
+
710
+ # T6: build manifest / tooling / test
711
+ if cat == "build_system":
712
+ return round(min(0.45, base_rel), 3)
713
+ if cat in ("tests", "tooling"):
714
+ return round(min(0.35, base_rel), 3)
715
+
716
+ # T7: no content classification — filesystem/structural signals only
717
+ # has_structural_signals: fan_in, churn, export — allows up to 0.54
718
+ # pure path/filename only — hard cap 0.39
719
+ if has_structural_signals:
720
+ return round(min(0.54, max(0.10, pre_bonus_combined / 2.0)), 3)
721
+ return round(min(0.39, max(0.10, pre_bonus_combined / 2.0)), 3)
722
+
723
+
724
+ def _build_file_signals(
725
+ file_class: Any,
726
+ path: str,
727
+ entry_paths: set,
728
+ fs_reasons: list,
729
+ sem_hub: float,
730
+ ) -> list[dict]:
731
+ """Minimal per-file signal breakdown: what contributed to this file's score."""
732
+ from sourcecode.file_classifier import JAVA_STEREOTYPE_CATEGORIES
733
+
734
+ signals: list[dict] = []
735
+
736
+ if path in entry_paths:
737
+ signals.append({"type": "runtime_entrypoint", "strength": "strong"})
738
+
739
+ if file_class:
740
+ cat = file_class.category
741
+ if cat in JAVA_STEREOTYPE_CATEGORIES:
742
+ signals.append({"type": "framework_annotation", "strength": "strong"})
743
+ elif cat in ("api_layer", "database_layer", "infrastructure"):
744
+ ev = [e for e in (file_class.evidence or [])[:2] if e]
745
+ signals.append({"type": "framework_import", "strength": "medium", "evidence": ev})
746
+ elif cat in ("application_logic",):
747
+ signals.append({"type": "code_definitions_with_imports", "strength": "medium"})
748
+ elif cat in ("domain_model",):
749
+ signals.append({"type": "domain_model_definitions", "strength": "medium"})
750
+ elif cat in ("build_system",):
751
+ signals.append({"type": "build_manifest", "strength": "medium"})
752
+
753
+ for r in fs_reasons:
754
+ r_lower = r.lower()
755
+ if "import centrality" in r_lower or "imported by" in r_lower:
756
+ signals.append({"type": "import_centrality", "strength": "medium"})
757
+ elif "hub module" in r_lower:
758
+ signals.append({"type": "hub_module", "strength": "medium"})
759
+ elif "recent churn" in r_lower:
760
+ signals.append({"type": "git_churn", "strength": "medium"})
761
+ elif "uncommitted" in r_lower:
762
+ signals.append({"type": "uncommitted_changes", "strength": "medium"})
763
+
764
+ if sem_hub >= 0.15:
765
+ signals.append({"type": "call_graph_hub", "strength": "strong"})
766
+
767
+ if not signals:
768
+ signals.append({"type": "filesystem_path", "strength": "weak"})
769
+
770
+ return signals
771
+
772
+
663
773
  def _file_relevance(sm: SourceMap, *, limit: int = _FILE_RELEVANCE_LIMIT) -> list[dict[str, Any]]:
664
774
  from sourcecode.ranking_engine import RankingEngine
665
775
 
@@ -692,13 +802,13 @@ def _file_relevance(sm: SourceMap, *, limit: int = _FILE_RELEVANCE_LIMIT) -> lis
692
802
  semantic_hub_scores[p] = h.get("importance_score", 0.0) / max_importance
693
803
 
694
804
  entry_paths = {ep.path for ep in sm.entry_points}
695
- # REST/MVC controllers are HTTP surface — surface before @Transactional services
696
- _rest_ctrl_paths = {
697
- ep.path for ep in sm.entry_points
698
- if getattr(ep, "kind", "") in {"rest_controller", "mvc_controller"}
699
- }
700
805
  scored: list[tuple[float, dict[str, Any]]] = []
701
806
 
807
+ from sourcecode.ranking_engine import (
808
+ compute_impact_score, resolve_runtime_impact, resolve_framework_signal,
809
+ _NORM_TARGET_HI,
810
+ )
811
+
702
812
  for path in sm.file_paths:
703
813
  file_class = classifier.classify(path)
704
814
  fs = engine.score(
@@ -711,96 +821,129 @@ def _file_relevance(sm: SourceMap, *, limit: int = _FILE_RELEVANCE_LIMIT) -> lis
711
821
  if fs.score < -50: # hard noise
712
822
  continue
713
823
 
714
- content_rel = file_class.relevance if file_class else 0.0
715
- # Semantic hub bonus: normalised call-graph centrality adds up to +0.30
716
- sem_hub = semantic_hub_scores.get(path, 0.0) * 0.30
717
- combined = fs.score + content_rel + sem_hub
718
- # REST controller boost: surface above @Transactional service files
719
- if path in _rest_ctrl_paths:
720
- combined += 2.0
721
-
722
- # M3: Structural importance scoring — priority over alphabetical ordering.
723
824
  stem = Path(path).stem
724
- stem_lower = stem.lower()
725
- path_lower = path.lower()
726
- # +10 application entry points
727
- if path in entry_paths and any(
728
- k in stem for k in ("Application", "Main", "Initializer", "Bootstrap", "Startup")
729
- ):
730
- combined += 10.0
731
- # +8 known base/infrastructure classes
732
- elif any(k in stem for k in (
733
- "GenericRestController", "GenericCRUDRestController", "GenericController",
734
- "AkitaBaseService", "BaseService", "FilterConfig", "AbstractController",
735
- )):
736
- combined += 8.0
737
- # +6 security configuration
738
- elif any(k in stem for k in (
739
- "SecurityConfig", "SecurityStrategy", "WebSecurityConfig", "SecurityFilter",
740
- "JwtFilter", "AuthConfig", "SecurityConfiguration",
741
- )):
742
- combined += 6.0
743
- # +2 shared utilities
744
- elif any(k in path_lower for k in ("util", "shared", "common", "helper", "constant")):
745
- combined += 2.0
746
-
747
- # Visibility threshold: require meaningful combined signal.
748
- # Exception: high/medium-confidence files with strong content relevance
749
- # can survive even if structural score is weak.
750
- if combined < _FILE_RELEVANCE_MIN_COMBINED:
825
+ cat = file_class.category if file_class else None
826
+
827
+ # ── Component 1: runtime_impact — execution-path role ─────────────────
828
+ runtime_impact = resolve_runtime_impact(cat)
829
+
830
+ # ── Component 2: dependency_centrality — call-graph importance ─────────
831
+ # Entry points treat external HTTP/CLI callers as high centrality.
832
+ # Isolated files (no semantic data) score 0.0 — negative weighting by omission.
833
+ dep_centrality = semantic_hub_scores.get(path, 0.0)
834
+ if path in entry_paths:
835
+ dep_centrality = max(dep_centrality, 0.8)
836
+
837
+ # ── Component 3: framework_signal_strength — annotation quality ─────────
838
+ fw_signal = resolve_framework_signal(cat)
839
+
840
+ # ── Component 4: change_type_severity — git churn as structural proxy ───
841
+ churn = git_churn.get(path, 0)
842
+ change_sev = min(churn / max(max_churn, 1), 1.0) * 0.8
843
+
844
+ # ── Component 5: test_risk_factor — no per-file coverage data ──────────
845
+ test_risk = 0.2
846
+
847
+ formula_raw = compute_impact_score(
848
+ runtime_impact, dep_centrality, fw_signal, change_sev, test_risk
849
+ )
850
+
851
+ # T1 override: confirmed production entrypoints → 0.92–1.00.
852
+ # Only runtime_core / cli_entrypoint categories justify scores ≥ 0.92.
853
+ if path in entry_paths and cat in ("runtime_core", "cli_entrypoint"):
854
+ score_val = round(
855
+ min(1.0, max(0.92, file_class.relevance if file_class else 0.92)), 3
856
+ )
857
+ else:
858
+ score_val = round(formula_raw, 3)
859
+
860
+ # Visibility threshold: formula score or high-relevance content exception.
861
+ if score_val < _FILE_RELEVANCE_MIN_COMBINED:
751
862
  if not (file_class
752
863
  and file_class.relevance > 0.45
753
864
  and file_class.confidence in {"high", "medium"}):
754
865
  continue
755
866
 
756
- # Suppress low-confidence auxiliary/config files unless structurally prominent
867
+ # Suppress low-confidence auxiliary/config files
757
868
  if (file_class
758
869
  and file_class.confidence == "low"
759
870
  and file_class.category in {"config", "auxiliary"}
760
- and combined < 0.45):
871
+ and score_val < 0.45):
761
872
  continue
762
873
 
763
- # For Java stereotype annotations use the table relevance directly —
764
- # the combined/2 formula would dilute the stereotype signal.
765
- from sourcecode.file_classifier import JAVA_STEREOTYPE_CATEGORIES
766
- if file_class and file_class.category in JAVA_STEREOTYPE_CATEGORIES:
767
- relevance_val = round(file_class.relevance, 3)
768
- else:
769
- relevance_val = round(max(0.0, min(1.0, combined / 2.0)), 3)
874
+ # relevance: content evidence only intentionally diverges from score
875
+ # when dep_centrality or churn are non-zero (score ≠ relevance invariant)
876
+ relevance_val = round(file_class.relevance, 3) if file_class else round(
877
+ min(0.39, max(0.10, runtime_impact)), 3
878
+ )
879
+
880
+ # sem_hub retained for signal reporting only — not used in score formula
881
+ sem_hub = semantic_hub_scores.get(path, 0.0) * 0.30
882
+ ranking_reasons = [r for r in fs.reasons if r != "source file"]
883
+ if sem_hub >= 0.15:
884
+ ranking_reasons.append("call graph hub")
885
+
886
+ signals = _build_file_signals(file_class, path, entry_paths, ranking_reasons, sem_hub)
770
887
 
771
888
  item: dict[str, Any] = {
772
889
  "path": path,
773
890
  "category": file_class.category if file_class else "source",
774
891
  "confidence": file_class.confidence if file_class else "low",
892
+ "score": score_val,
775
893
  "relevance": relevance_val,
776
- "score": relevance_val,
777
894
  "reason": file_class.reason if file_class else (fs.reasons[0] if fs.reasons else "source file"),
778
895
  "evidence": file_class.evidence if file_class else [],
896
+ "signals": signals,
779
897
  }
780
898
 
781
- ranking_reasons = [r for r in fs.reasons if r != "source file"]
782
- if sem_hub >= 0.15:
783
- ranking_reasons.append("call graph hub")
784
899
  if ranking_reasons:
785
900
  item["ranking_reasons"] = ranking_reasons
786
901
 
787
- # Override metadata for known M3 base controller classes
902
+ # Override: universal base controller classes score as runtime_core
788
903
  if any(k in stem for k in ("GenericRestController", "GenericCRUDRestController")):
789
904
  item["category"] = "runtime_core"
790
- item["relevance"] = 0.95
791
905
  item["score"] = 0.95
906
+ item["relevance"] = 0.95
792
907
  item["reason"] = (
793
908
  "base class for all REST controllers — extends this to get "
794
909
  "centralized exception handling via handlerException()"
795
910
  )
796
911
  item["evidence"] = ["base_rest_controller"]
797
912
  item["ranking_reasons"] = ["universal base class", "exception handling contract"]
913
+ item["signals"] = [{"type": "framework_annotation", "strength": "strong"}]
798
914
 
799
- scored.append((combined, item))
915
+ # sort_key = final score (rank consistency: score order = output order)
916
+ scored.append((item["score"], item))
800
917
 
801
- # Deterministic sort: score desc, then path asc
918
+ # Initial sort: score desc, path asc for deterministic tie-break
802
919
  scored.sort(key=lambda x: (-x[0], x[1]["path"]))
803
920
 
921
+ # Normalization: enforce minimum spread ≥ 0.4 among non-T1 files.
922
+ # T1 files (confirmed entrypoints, 0.92–1.0) are excluded — already correct.
923
+ # Prevents score compression when structural signals (--semantics, git) absent.
924
+ _nonep = [(sk, it) for sk, it in scored if it["path"] not in entry_paths]
925
+ if len(_nonep) > 1:
926
+ _vals = [it["score"] for _, it in _nonep]
927
+ _lo, _hi = min(_vals), max(_vals)
928
+ _spread = _hi - _lo
929
+ if _spread < 0.40:
930
+ _top_cat = max(_nonep, key=lambda x: x[1]["score"])[1].get("category", "")
931
+ _target_hi = _NORM_TARGET_HI.get(_top_cat, 0.60)
932
+ _target_lo = max(0.10, _target_hi - 0.50)
933
+ if _spread > 0:
934
+ _scale = (_target_hi - _target_lo) / _spread
935
+ for _, it in _nonep:
936
+ it["score"] = round(
937
+ max(0.0, min(1.0, _target_lo + (it["score"] - _lo) * _scale)), 3
938
+ )
939
+ else:
940
+ _mid = round((_target_hi + _target_lo) / 2.0, 3)
941
+ for _, it in _nonep:
942
+ it["score"] = _mid
943
+
944
+ # Re-sort by final score to guarantee rank consistency after normalization
945
+ scored.sort(key=lambda x: (-x[1]["score"], x[1]["path"]))
946
+
804
947
  # Diversity cap: at most half the budget from any single category.
805
948
  # Prevents 10/10 controllers drowning out services, repositories, domain.
806
949
  _CAT_CAP = max(1, limit // 2)