sourcecode 1.30.14__tar.gz → 1.30.16__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 (193) hide show
  1. {sourcecode-1.30.14 → sourcecode-1.30.16}/PKG-INFO +48 -25
  2. {sourcecode-1.30.14 → sourcecode-1.30.16}/README.md +47 -24
  3. {sourcecode-1.30.14 → sourcecode-1.30.16}/pyproject.toml +1 -1
  4. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/cli.py +34 -3
  6. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/flow_analyzer.py +49 -24
  7. sourcecode-1.30.16/src/sourcecode/pr_comment_renderer.py +369 -0
  8. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/prepare_context.py +63 -15
  9. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_v1_10_regressions.py +1 -1
  10. {sourcecode-1.30.14 → sourcecode-1.30.16}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  11. {sourcecode-1.30.14 → sourcecode-1.30.16}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  12. {sourcecode-1.30.14 → sourcecode-1.30.16}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  13. {sourcecode-1.30.14 → sourcecode-1.30.16}/.continue-here.md +0 -0
  14. {sourcecode-1.30.14 → sourcecode-1.30.16}/.github/workflows/build-windows.yml +0 -0
  15. {sourcecode-1.30.14 → sourcecode-1.30.16}/.gitignore +0 -0
  16. {sourcecode-1.30.14 → sourcecode-1.30.16}/.ruff.toml +0 -0
  17. {sourcecode-1.30.14 → sourcecode-1.30.16}/CONTRIBUTING.md +0 -0
  18. {sourcecode-1.30.14 → sourcecode-1.30.16}/LICENSE +0 -0
  19. {sourcecode-1.30.14 → sourcecode-1.30.16}/SECURITY.md +0 -0
  20. {sourcecode-1.30.14 → sourcecode-1.30.16}/docs/privacy.md +0 -0
  21. {sourcecode-1.30.14 → sourcecode-1.30.16}/docs/schema.md +0 -0
  22. {sourcecode-1.30.14 → sourcecode-1.30.16}/raw +0 -0
  23. {sourcecode-1.30.14 → sourcecode-1.30.16}/run_cli.py +0 -0
  24. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/adaptive_scanner.py +0 -0
  25. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/architecture_analyzer.py +0 -0
  26. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/architecture_summary.py +0 -0
  27. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/ast_extractor.py +0 -0
  28. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/classifier.py +0 -0
  29. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/code_notes_analyzer.py +0 -0
  30. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/confidence_analyzer.py +0 -0
  31. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/context_scorer.py +0 -0
  32. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/context_summarizer.py +0 -0
  33. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/contract_model.py +0 -0
  34. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/contract_pipeline.py +0 -0
  35. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/coverage_parser.py +0 -0
  36. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/dependency_analyzer.py +0 -0
  37. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/__init__.py +0 -0
  38. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/base.py +0 -0
  39. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/csproj_parser.py +0 -0
  40. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/dart.py +0 -0
  41. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/dotnet.py +0 -0
  42. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/elixir.py +0 -0
  43. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/go.py +0 -0
  44. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/heuristic.py +0 -0
  45. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/hybrid.py +0 -0
  46. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/java.py +0 -0
  47. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/jvm_ext.py +0 -0
  48. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/nodejs.py +0 -0
  49. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/parsers.py +0 -0
  50. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/php.py +0 -0
  51. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/project.py +0 -0
  52. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/python.py +0 -0
  53. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/ruby.py +0 -0
  54. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/rust.py +0 -0
  55. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/systems.py +0 -0
  56. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/terraform.py +0 -0
  57. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/detectors/tooling.py +0 -0
  58. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/doc_analyzer.py +0 -0
  59. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/entrypoint_classifier.py +0 -0
  60. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/env_analyzer.py +0 -0
  61. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/file_classifier.py +0 -0
  62. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/git_analyzer.py +0 -0
  63. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/graph_analyzer.py +0 -0
  64. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/metrics_analyzer.py +0 -0
  65. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/progress.py +0 -0
  66. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/ranking_engine.py +0 -0
  67. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/redactor.py +0 -0
  68. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/relevance_scorer.py +0 -0
  69. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/repo_classifier.py +0 -0
  70. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/repository_ir.py +0 -0
  71. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/runtime_classifier.py +0 -0
  72. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/scanner.py +0 -0
  73. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/schema.py +0 -0
  74. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/semantic_analyzer.py +0 -0
  75. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/serializer.py +0 -0
  76. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/summarizer.py +0 -0
  77. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/telemetry/__init__.py +0 -0
  78. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/telemetry/config.py +0 -0
  79. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/telemetry/consent.py +0 -0
  80. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/telemetry/events.py +0 -0
  81. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/telemetry/filters.py +0 -0
  82. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/telemetry/transport.py +0 -0
  83. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/tree_utils.py +0 -0
  84. {sourcecode-1.30.14 → sourcecode-1.30.16}/src/sourcecode/workspace.py +0 -0
  85. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/__init__.py +0 -0
  86. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/conftest.py +0 -0
  87. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/coverage.xml +0 -0
  88. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  89. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/fastapi_app/src/main.py +0 -0
  90. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  91. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/go_service/go.mod +0 -0
  92. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/jacoco.xml +0 -0
  93. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/latin1_sample.java +0 -0
  94. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/latin1_sample_iso.java +0 -0
  95. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/lcov.info +0 -0
  96. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  97. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/nextjs_app/package.json +0 -0
  98. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  99. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  100. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  101. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  102. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  103. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  104. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  105. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  106. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  107. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  108. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  109. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  110. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  111. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  112. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  113. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  114. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  115. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  116. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  117. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  118. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  119. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  120. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  121. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  122. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  123. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  124. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  125. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  126. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  127. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  128. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  129. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  130. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  131. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_architecture_analyzer.py +0 -0
  132. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_architecture_summary.py +0 -0
  133. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_ast_extractor.py +0 -0
  134. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_block1_reliability.py +0 -0
  135. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_block2_coverage.py +0 -0
  136. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_block5_quality.py +0 -0
  137. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_bug_fixes_v16.py +0 -0
  138. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_classifier.py +0 -0
  139. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_cli.py +0 -0
  140. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_code_notes_analyzer.py +0 -0
  141. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_context_scorer.py +0 -0
  142. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_contract_pipeline.py +0 -0
  143. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_coverage_parser.py +0 -0
  144. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_cross_consistency.py +0 -0
  145. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_dependency_analyzer_node_python.py +0 -0
  146. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_dependency_analyzer_polyglot.py +0 -0
  147. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_dependency_schema.py +0 -0
  148. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_dotnet.py +0 -0
  149. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_go_rust_java.py +0 -0
  150. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_nodejs.py +0 -0
  151. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_php_ruby_dart.py +0 -0
  152. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_python.py +0 -0
  153. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_universal_managed.py +0 -0
  154. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detector_universal_systems.py +0 -0
  155. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_detectors_base.py +0 -0
  156. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_doc_analyzer_jsdom.py +0 -0
  157. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_doc_analyzer_python.py +0 -0
  158. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_encoding_regression.py +0 -0
  159. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_graph_analyzer_polyglot.py +0 -0
  160. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_graph_analyzer_python_node.py +0 -0
  161. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_graph_schema.py +0 -0
  162. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_hybrid_inference.py +0 -0
  163. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration.py +0 -0
  164. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_dependencies.py +0 -0
  165. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_detection.py +0 -0
  166. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_docs.py +0 -0
  167. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_graph_modules.py +0 -0
  168. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_lqn.py +0 -0
  169. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_metrics.py +0 -0
  170. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_multistack.py +0 -0
  171. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_semantics.py +0 -0
  172. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_integration_universal.py +0 -0
  173. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_java_spring_integration.py +0 -0
  174. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_metrics_analyzer.py +0 -0
  175. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_packaging.py +0 -0
  176. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_phase1_improvements.py +0 -0
  177. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_pipeline_integrity.py +0 -0
  178. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_real_projects.py +0 -0
  179. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_redactor.py +0 -0
  180. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_repository_ir.py +0 -0
  181. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_scanner.py +0 -0
  182. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_schema.py +0 -0
  183. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_schema_normalization.py +0 -0
  184. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_semantic_analyzer_node.py +0 -0
  185. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_semantic_analyzer_python.py +0 -0
  186. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_semantic_import_resolution.py +0 -0
  187. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_semantic_schema.py +0 -0
  188. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_signal_hierarchy.py +0 -0
  189. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_summarizer.py +0 -0
  190. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_surface_honesty.py +0 -0
  191. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_task_differentiation.py +0 -0
  192. {sourcecode-1.30.14 → sourcecode-1.30.16}/tests/test_telemetry.py +0 -0
  193. {sourcecode-1.30.14 → sourcecode-1.30.16}/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.14
3
+ Version: 1.30.16
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.14-blue)
224
+ ![Version](https://img.shields.io/badge/version-1.30.16-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.14
260
+ # sourcecode 1.30.16
261
261
  ```
262
262
 
263
263
  ---
@@ -353,6 +353,7 @@ sourcecode prepare-context TASK [PATH] [OPTIONS]
353
353
  |--------|-------------|
354
354
  | `--since REF` | Git ref for `delta` task (e.g. `HEAD~3`, `main`, `v1.2.0`). Required for `delta`; ignored for other tasks. |
355
355
  | `--symptom TEXT` | *(fix-bug only)* Keyword hint for the bug — boosts matching files and surfaces related code notes. |
356
+ | `--format TEXT` | Output format: `json` (default) \| `github-comment` (Markdown PR comment, `review-pr` only). |
356
357
  | `--llm-prompt` | Append a ready-to-use LLM prompt to the output. |
357
358
  | `--dry-run` | Show what would be analyzed without running it. |
358
359
  | `--copy` / `-c` | Copy output to clipboard after a successful run. |
@@ -374,6 +375,9 @@ sourcecode prepare-context delta . --since main
374
375
  # Onboard with a ready-to-paste LLM prompt
375
376
  sourcecode prepare-context onboard --llm-prompt
376
377
 
378
+ # PR analysis as a GitHub Markdown comment (paste directly into PR)
379
+ sourcecode prepare-context review-pr --since main --format github-comment
380
+
377
381
  # List all tasks
378
382
  sourcecode prepare-context --task-help
379
383
  ```
@@ -444,22 +448,25 @@ sourcecode prepare-context review-pr
444
448
  "name": "Order",
445
449
  "entry_point": {
446
450
  "step": "OrderController.createOrder",
447
- "notes": ["condition: authorization check present (@PreAuthorize / @Secured)"]
451
+ "notes": [
452
+ { "note": "condition: authorization check present (@PreAuthorize / @Secured)",
453
+ "epistemic_level": "STRUCTURAL SIGNAL" }
454
+ ]
448
455
  },
449
456
  "path": [
450
457
  {
451
458
  "step": "ShippingService.process",
452
459
  "notes": [
453
- "branch: Spring cache may short-circuit downstream call",
454
- "async: runs in separate thread (@Async)"
460
+ { "note": "branch: Spring cache annotation present downstream call may be short-circuited",
461
+ "epistemic_level": "STRUCTURAL SIGNAL" },
462
+ { "note": "async: @Async annotation present — runs in separate thread",
463
+ "epistemic_level": "STRUCTURAL SIGNAL" }
455
464
  ]
456
465
  },
457
- {
458
- "step": "OrderRepository.save",
459
- "notes": []
460
- }
466
+ { "step": "OrderRepository.save", "notes": [] }
461
467
  ],
462
- "end_state": "DB write"
468
+ "end_state": "DB write",
469
+ "end_state_epistemic_level": "INFERRED (LOW CONFIDENCE)"
463
470
  }
464
471
  ]
465
472
  }
@@ -474,17 +481,33 @@ sourcecode prepare-context review-pr
474
481
 
475
482
  **Runtime signals detected per step:**
476
483
 
477
- | Signal | Example code | Note emitted |
478
- |--------|-------------|--------------|
479
- | Auth guard | `@PreAuthorize`, `@Secured`, `isAuthenticated()` | `condition: authorization check present` |
480
- | Feature flag | `featureFlag.isEnabled()`, `FeatureToggle` | `condition: feature flag gates execution` |
481
- | Null/empty guard | `if (x == null) return` | `condition: null/empty guard with early return` |
482
- | Spring cache | `@Cacheable`, `@CacheEvict` | `branch: Spring cache may short-circuit downstream call` |
483
- | Optional absence | `Optional<>`, `.orElseThrow()` | `branch: result may be absent (Optional)` |
484
- | Async thread | `@Async`, `CompletableFuture` | `async: runs in separate thread (@Async)` |
485
- | Event publishing | `publishEvent()`, `applicationEventPublisher` | `async: Spring application event emitted` |
486
- | Kafka | `kafkaTemplate.send()` | `async: Kafka message produced` |
487
- | RabbitMQ | `rabbitTemplate.send()` | `async: RabbitMQ message sent` |
484
+ | Signal | Example code | Note emitted | Epistemic level |
485
+ |--------|-------------|--------------|-----------------|
486
+ | Auth guard | `@PreAuthorize`, `@Secured` | `condition: authorization check present (@PreAuthorize / @Secured)` | `STRUCTURAL SIGNAL` |
487
+ | Auth context read | `isAuthenticated()`, `SecurityContextHolder` | `condition: reads authentication context` | `STRUCTURAL SIGNAL` |
488
+ | Feature flag | `featureFlag.isEnabled()`, `FeatureToggle` | `condition: feature flag gates execution` | `INFERRED (LOW CONFIDENCE)` |
489
+ | Null/empty guard | `if (x == null) return` | `condition: null/empty guard with early return` | `STRUCTURAL SIGNAL` |
490
+ | Spring cache | `@Cacheable`, `@CacheEvict` | `branch: Spring cache annotation present — downstream call may be short-circuited` | `STRUCTURAL SIGNAL` |
491
+ | Manual cache | `cache.get()`, `cacheManager.` | `branch: manual cache lookup detected — downstream call may be short-circuited` | `INFERRED (LOW CONFIDENCE)` |
492
+ | Optional absence | `Optional<>`, `.orElseThrow()` | `branch: Optional type in use — result may be absent` | `STRUCTURAL SIGNAL` |
493
+ | Async thread | `@Async` | `async: @Async annotation present — runs in separate thread` | `STRUCTURAL SIGNAL` |
494
+ | CompletableFuture | `CompletableFuture`, `.supplyAsync()` | `async: CompletableFuture detected — non-blocking execution` | `STRUCTURAL SIGNAL` |
495
+ | Event publishing | `publishEvent()`, `applicationEventPublisher` | `async: Spring application event emitted` | `STRUCTURAL SIGNAL` |
496
+ | Kafka | `kafkaTemplate.`, `KafkaProducer` | `async: Kafka producer detected` | `STRUCTURAL SIGNAL` |
497
+ | RabbitMQ | `rabbitTemplate.`, `amqpTemplate.` | `async: RabbitMQ producer detected` | `STRUCTURAL SIGNAL` |
498
+
499
+ **Epistemic contract:**
500
+
501
+ Every output field in `review-pr` carries an explicit `epistemic_level`:
502
+
503
+ | Level | Meaning |
504
+ |-------|---------|
505
+ | `FACT` | Directly observed in diff (file present, config changed) |
506
+ | `STRUCTURAL SIGNAL` | Annotation or type-system evidence in source (`@Service`, `@Transactional`, injection) |
507
+ | `INFERRED (LOW CONFIDENCE)` | Heuristic pattern match — no full structural proof |
508
+ | `OMITTED` | Insufficient evidence — field not emitted |
509
+
510
+ No field blends certainty levels without labeling. `end_state` (e.g. `"DB write"`) is always accompanied by `end_state_epistemic_level: "INFERRED (LOW CONFIDENCE)"` — it is a keyword-match heuristic, not an AST-verified fact.
488
511
 
489
512
  **Other `review-pr` output fields:**
490
513
 
@@ -492,9 +515,9 @@ sourcecode prepare-context review-pr
492
515
  |-------|-------------|
493
516
  | `review_hotspots` | Top changed files ranked by impact score |
494
517
  | `suggested_review_order` | Security → API → Service → Persistence → Config |
495
- | `security_impact` | Changed files touching the security surface |
496
- | `transactional_impact` | Files crossing transaction boundaries |
497
- | `test_coverage_risk` | Changed source files with no corresponding test |
518
+ | `security_impact` | Changed security-classified files (`epistemic_level: STRUCTURAL SIGNAL`) + risk note (`INFERRED (LOW CONFIDENCE)`) |
519
+ | `transactional_impact` | Changed service/business-logic files with possible transaction boundary effect |
520
+ | `test_coverage_risk` | Changed source files with no corresponding test (`epistemic_level: INFERRED (LOW CONFIDENCE)`) |
498
521
  | `affected_modules` | DDD domain modules touched by the change |
499
522
 
500
523
  ---
@@ -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.14-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.30.16-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.14
41
+ # sourcecode 1.30.16
42
42
  ```
43
43
 
44
44
  ---
@@ -134,6 +134,7 @@ sourcecode prepare-context TASK [PATH] [OPTIONS]
134
134
  |--------|-------------|
135
135
  | `--since REF` | Git ref for `delta` task (e.g. `HEAD~3`, `main`, `v1.2.0`). Required for `delta`; ignored for other tasks. |
136
136
  | `--symptom TEXT` | *(fix-bug only)* Keyword hint for the bug — boosts matching files and surfaces related code notes. |
137
+ | `--format TEXT` | Output format: `json` (default) \| `github-comment` (Markdown PR comment, `review-pr` only). |
137
138
  | `--llm-prompt` | Append a ready-to-use LLM prompt to the output. |
138
139
  | `--dry-run` | Show what would be analyzed without running it. |
139
140
  | `--copy` / `-c` | Copy output to clipboard after a successful run. |
@@ -155,6 +156,9 @@ sourcecode prepare-context delta . --since main
155
156
  # Onboard with a ready-to-paste LLM prompt
156
157
  sourcecode prepare-context onboard --llm-prompt
157
158
 
159
+ # PR analysis as a GitHub Markdown comment (paste directly into PR)
160
+ sourcecode prepare-context review-pr --since main --format github-comment
161
+
158
162
  # List all tasks
159
163
  sourcecode prepare-context --task-help
160
164
  ```
@@ -225,22 +229,25 @@ sourcecode prepare-context review-pr
225
229
  "name": "Order",
226
230
  "entry_point": {
227
231
  "step": "OrderController.createOrder",
228
- "notes": ["condition: authorization check present (@PreAuthorize / @Secured)"]
232
+ "notes": [
233
+ { "note": "condition: authorization check present (@PreAuthorize / @Secured)",
234
+ "epistemic_level": "STRUCTURAL SIGNAL" }
235
+ ]
229
236
  },
230
237
  "path": [
231
238
  {
232
239
  "step": "ShippingService.process",
233
240
  "notes": [
234
- "branch: Spring cache may short-circuit downstream call",
235
- "async: runs in separate thread (@Async)"
241
+ { "note": "branch: Spring cache annotation present downstream call may be short-circuited",
242
+ "epistemic_level": "STRUCTURAL SIGNAL" },
243
+ { "note": "async: @Async annotation present — runs in separate thread",
244
+ "epistemic_level": "STRUCTURAL SIGNAL" }
236
245
  ]
237
246
  },
238
- {
239
- "step": "OrderRepository.save",
240
- "notes": []
241
- }
247
+ { "step": "OrderRepository.save", "notes": [] }
242
248
  ],
243
- "end_state": "DB write"
249
+ "end_state": "DB write",
250
+ "end_state_epistemic_level": "INFERRED (LOW CONFIDENCE)"
244
251
  }
245
252
  ]
246
253
  }
@@ -255,17 +262,33 @@ sourcecode prepare-context review-pr
255
262
 
256
263
  **Runtime signals detected per step:**
257
264
 
258
- | Signal | Example code | Note emitted |
259
- |--------|-------------|--------------|
260
- | Auth guard | `@PreAuthorize`, `@Secured`, `isAuthenticated()` | `condition: authorization check present` |
261
- | Feature flag | `featureFlag.isEnabled()`, `FeatureToggle` | `condition: feature flag gates execution` |
262
- | Null/empty guard | `if (x == null) return` | `condition: null/empty guard with early return` |
263
- | Spring cache | `@Cacheable`, `@CacheEvict` | `branch: Spring cache may short-circuit downstream call` |
264
- | Optional absence | `Optional<>`, `.orElseThrow()` | `branch: result may be absent (Optional)` |
265
- | Async thread | `@Async`, `CompletableFuture` | `async: runs in separate thread (@Async)` |
266
- | Event publishing | `publishEvent()`, `applicationEventPublisher` | `async: Spring application event emitted` |
267
- | Kafka | `kafkaTemplate.send()` | `async: Kafka message produced` |
268
- | RabbitMQ | `rabbitTemplate.send()` | `async: RabbitMQ message sent` |
265
+ | Signal | Example code | Note emitted | Epistemic level |
266
+ |--------|-------------|--------------|-----------------|
267
+ | Auth guard | `@PreAuthorize`, `@Secured` | `condition: authorization check present (@PreAuthorize / @Secured)` | `STRUCTURAL SIGNAL` |
268
+ | Auth context read | `isAuthenticated()`, `SecurityContextHolder` | `condition: reads authentication context` | `STRUCTURAL SIGNAL` |
269
+ | Feature flag | `featureFlag.isEnabled()`, `FeatureToggle` | `condition: feature flag gates execution` | `INFERRED (LOW CONFIDENCE)` |
270
+ | Null/empty guard | `if (x == null) return` | `condition: null/empty guard with early return` | `STRUCTURAL SIGNAL` |
271
+ | Spring cache | `@Cacheable`, `@CacheEvict` | `branch: Spring cache annotation present — downstream call may be short-circuited` | `STRUCTURAL SIGNAL` |
272
+ | Manual cache | `cache.get()`, `cacheManager.` | `branch: manual cache lookup detected — downstream call may be short-circuited` | `INFERRED (LOW CONFIDENCE)` |
273
+ | Optional absence | `Optional<>`, `.orElseThrow()` | `branch: Optional type in use — result may be absent` | `STRUCTURAL SIGNAL` |
274
+ | Async thread | `@Async` | `async: @Async annotation present — runs in separate thread` | `STRUCTURAL SIGNAL` |
275
+ | CompletableFuture | `CompletableFuture`, `.supplyAsync()` | `async: CompletableFuture detected — non-blocking execution` | `STRUCTURAL SIGNAL` |
276
+ | Event publishing | `publishEvent()`, `applicationEventPublisher` | `async: Spring application event emitted` | `STRUCTURAL SIGNAL` |
277
+ | Kafka | `kafkaTemplate.`, `KafkaProducer` | `async: Kafka producer detected` | `STRUCTURAL SIGNAL` |
278
+ | RabbitMQ | `rabbitTemplate.`, `amqpTemplate.` | `async: RabbitMQ producer detected` | `STRUCTURAL SIGNAL` |
279
+
280
+ **Epistemic contract:**
281
+
282
+ Every output field in `review-pr` carries an explicit `epistemic_level`:
283
+
284
+ | Level | Meaning |
285
+ |-------|---------|
286
+ | `FACT` | Directly observed in diff (file present, config changed) |
287
+ | `STRUCTURAL SIGNAL` | Annotation or type-system evidence in source (`@Service`, `@Transactional`, injection) |
288
+ | `INFERRED (LOW CONFIDENCE)` | Heuristic pattern match — no full structural proof |
289
+ | `OMITTED` | Insufficient evidence — field not emitted |
290
+
291
+ No field blends certainty levels without labeling. `end_state` (e.g. `"DB write"`) is always accompanied by `end_state_epistemic_level: "INFERRED (LOW CONFIDENCE)"` — it is a keyword-match heuristic, not an AST-verified fact.
269
292
 
270
293
  **Other `review-pr` output fields:**
271
294
 
@@ -273,9 +296,9 @@ sourcecode prepare-context review-pr
273
296
  |-------|-------------|
274
297
  | `review_hotspots` | Top changed files ranked by impact score |
275
298
  | `suggested_review_order` | Security → API → Service → Persistence → Config |
276
- | `security_impact` | Changed files touching the security surface |
277
- | `transactional_impact` | Files crossing transaction boundaries |
278
- | `test_coverage_risk` | Changed source files with no corresponding test |
299
+ | `security_impact` | Changed security-classified files (`epistemic_level: STRUCTURAL SIGNAL`) + risk note (`INFERRED (LOW CONFIDENCE)`) |
300
+ | `transactional_impact` | Changed service/business-logic files with possible transaction boundary effect |
301
+ | `test_coverage_risk` | Changed source files with no corresponding test (`epistemic_level: INFERRED (LOW CONFIDENCE)`) |
279
302
  | `affected_modules` | DDD domain modules touched by the change |
280
303
 
281
304
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.30.14"
7
+ version = "1.30.16"
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.14"
3
+ __version__ = "1.30.16"
@@ -161,7 +161,7 @@ Compressed AI-ready context for Java/Spring enterprise codebases.
161
161
  # Known subcommand names — tokens matching these are routed as subcommands,
162
162
  # not consumed as a repository path.
163
163
  _SUBCOMMANDS: frozenset[str] = frozenset(
164
- {"telemetry", "prepare-context", "version", "config", "analyze"}
164
+ {"telemetry", "prepare-context", "version", "config", "analyze", "repo-ir"}
165
165
  )
166
166
 
167
167
  # Mutable container holding the path extracted by _preprocess_argv().
@@ -1513,10 +1513,31 @@ def main(
1513
1513
  data = redact_dict(data)
1514
1514
  content = json.dumps(data, indent=2, ensure_ascii=False)
1515
1515
  elif compact:
1516
+ if changed_only and _allowed_changed_files:
1517
+ sm = _replace(sm,
1518
+ file_paths=[p for p in sm.file_paths if p in _allowed_changed_files],
1519
+ entry_points=[ep for ep in sm.entry_points if ep.path in _allowed_changed_files],
1520
+ code_notes=[n for n in sm.code_notes if n.path in _allowed_changed_files],
1521
+ )
1516
1522
  data = compact_view(sm, no_tree=no_tree, full=full)
1517
1523
  if not no_redact:
1518
1524
  data = redact_dict(data)
1519
- content = json.dumps(data, indent=2, ensure_ascii=False)
1525
+ if format == "yaml":
1526
+ from io import StringIO
1527
+ from ruamel.yaml import YAML as _YAML
1528
+ _yaml = _YAML()
1529
+ _yaml.default_flow_style = False
1530
+ _yaml.representer.add_representer(
1531
+ type(None),
1532
+ lambda dumper, data_val: dumper.represent_scalar(
1533
+ "tag:yaml.org,2002:null", "null"
1534
+ ),
1535
+ )
1536
+ _stream = StringIO()
1537
+ _yaml.dump(data, _stream)
1538
+ content = _stream.getvalue()
1539
+ else:
1540
+ content = json.dumps(data, indent=2, ensure_ascii=False)
1520
1541
  else:
1521
1542
  raw_dict = standard_view(sm, include_tree=tree and not no_tree)
1522
1543
  if not no_redact:
@@ -1622,6 +1643,11 @@ def prepare_context_cmd(
1622
1643
  "--symptom",
1623
1644
  help="(fix-bug) Keyword hint for the bug: boosts matching files and surfaces related code notes.",
1624
1645
  ),
1646
+ format: Optional[str] = typer.Option(
1647
+ None,
1648
+ "--format",
1649
+ help="Output format: json (default) | github-comment (Markdown PR comment for review-pr task)",
1650
+ ),
1625
1651
  ) -> None:
1626
1652
  """Task-specific context for AI coding agents.
1627
1653
 
@@ -1895,7 +1921,12 @@ def prepare_context_cmd(
1895
1921
  if llm_prompt:
1896
1922
  out["llm_prompt"] = builder.render_prompt(output)
1897
1923
 
1898
- _pc_content = json.dumps(out, indent=2, ensure_ascii=False)
1924
+ if format == "github-comment" and task == "review-pr":
1925
+ from sourcecode.pr_comment_renderer import render_github_comment
1926
+ _pc_content = render_github_comment(out)
1927
+ else:
1928
+ _pc_content = json.dumps(out, indent=2, ensure_ascii=False)
1929
+
1899
1930
  if output_path is not None:
1900
1931
  output_path.write_text(_pc_content, encoding="utf-8")
1901
1932
  else:
@@ -33,43 +33,57 @@ _METHOD_NAME_RE = re.compile(
33
33
  r'(\w+)\s*\(',
34
34
  )
35
35
 
36
- # Runtime signal patterns: (compiled_regex, note_text)
37
- # Only signals with explicit code evidence — no inference.
38
- # Three categories: condition | branch | async
39
- _RUNTIME_SIGNALS: list[tuple[re.Pattern, str]] = [
36
+ # Runtime signal patterns: (compiled_regex, note_text, epistemic_level)
37
+ # epistemic_level follows the 4-value contract:
38
+ # STRUCTURAL SIGNAL — annotation or type-system evidence directly observed in source
39
+ # INFERRED (LOW CONFIDENCE) heuristic code-pattern match; no full structural proof
40
+ _RUNTIME_SIGNALS: list[tuple[re.Pattern, str, str]] = [
40
41
  # ── Conditional / auth guards ─────────────────────────────────────────────
41
42
  (re.compile(r'@PreAuthorize|@Secured|@RolesAllowed', re.IGNORECASE),
42
- "condition: authorization check present (@PreAuthorize / @Secured)"),
43
+ "condition: authorization check present (@PreAuthorize / @Secured)",
44
+ "STRUCTURAL SIGNAL"),
43
45
  (re.compile(r'isAuthenticated\(\)|hasRole\(|hasAuthority\(|SecurityContextHolder', re.IGNORECASE),
44
- "condition: reads authentication context"),
46
+ "condition: reads authentication context",
47
+ "STRUCTURAL SIGNAL"),
45
48
  (re.compile(r'featureFlag|FeatureToggle|\.isEnabled\s*\(|\.isActive\s*\(', re.IGNORECASE),
46
- "condition: feature flag gates execution"),
49
+ "condition: feature flag gates execution",
50
+ "INFERRED (LOW CONFIDENCE)"),
47
51
  # Null/empty guard with early return — matches if (...null/empty...) return/throw on same line
48
52
  (re.compile(r'if\s*\([^)]*(?:==\s*null|!=\s*null|isEmpty\s*\(\)|isBlank\s*\(\))[^)]*\)'
49
53
  r'\s*(?:\{?\s*)?(?:return|throw)\b', re.IGNORECASE),
50
- "condition: null/empty guard with early return"),
54
+ "condition: null/empty guard with early return",
55
+ "STRUCTURAL SIGNAL"),
51
56
 
52
57
  # ── Optional execution / branching ────────────────────────────────────────
53
58
  (re.compile(r'@Cacheable|@CacheEvict|@CachePut', re.IGNORECASE),
54
- "branch: Spring cache may short-circuit downstream call"),
59
+ "branch: Spring cache annotation present downstream call may be short-circuited",
60
+ "STRUCTURAL SIGNAL"),
55
61
  (re.compile(r'\.getIfPresent\s*\(|cache\.get\s*\(|cacheManager\.', re.IGNORECASE),
56
- "branch: manual cache lookup may short-circuit"),
62
+ "branch: manual cache lookup detected — downstream call may be short-circuited",
63
+ "INFERRED (LOW CONFIDENCE)"),
57
64
  (re.compile(r'Optional\s*<|\.orElseThrow\s*\(|\.orElseGet\s*\(|\.orElse\s*\(', re.IGNORECASE),
58
- "branch: result may be absent (Optional)"),
65
+ "branch: Optional type in use — result may be absent",
66
+ "STRUCTURAL SIGNAL"),
59
67
 
60
68
  # ── Async / side effects ──────────────────────────────────────────────────
61
69
  (re.compile(r'@Async\b'),
62
- "async: runs in separate thread (@Async)"),
70
+ "async: @Async annotation present — runs in separate thread",
71
+ "STRUCTURAL SIGNAL"),
63
72
  (re.compile(r'CompletableFuture|\.supplyAsync\s*\(|\.runAsync\s*\('),
64
- "async: non-blocking future-based execution"),
73
+ "async: CompletableFuture detected — non-blocking execution",
74
+ "STRUCTURAL SIGNAL"),
65
75
  (re.compile(r'\basync\s+def\b|\bawait\b', re.IGNORECASE),
66
- "async: non-blocking (async/await)"),
76
+ "async: async/await detected — non-blocking execution",
77
+ "STRUCTURAL SIGNAL"),
67
78
  (re.compile(r'publishEvent\s*\(|applicationEventPublisher|eventPublisher\.', re.IGNORECASE),
68
- "async: Spring application event emitted"),
79
+ "async: Spring application event emitted",
80
+ "STRUCTURAL SIGNAL"),
69
81
  (re.compile(r'kafkaTemplate\.|KafkaProducer|@KafkaListener', re.IGNORECASE),
70
- "async: Kafka message produced"),
82
+ "async: Kafka producer detected",
83
+ "STRUCTURAL SIGNAL"),
71
84
  (re.compile(r'rabbitTemplate\.|amqpTemplate\.|@RabbitListener', re.IGNORECASE),
72
- "async: RabbitMQ message sent"),
85
+ "async: RabbitMQ producer detected",
86
+ "STRUCTURAL SIGNAL"),
73
87
  ]
74
88
 
75
89
 
@@ -98,18 +112,19 @@ def _read_safe(root: Path, rel_path: str) -> str:
98
112
  return ""
99
113
 
100
114
 
101
- def _collect_runtime_notes(content: str, lang: str) -> list[str]:
115
+ def _collect_runtime_notes(content: str, lang: str) -> list[dict]:
102
116
  """Scan comment-stripped content for explicit runtime behavior signals.
103
117
 
104
118
  Returns only notes backed by a direct code pattern match.
119
+ Each entry: {note: str, epistemic_level: str}.
105
120
  Returns [] when no signals are found.
106
121
  """
107
122
  clean = _strip_comments(content, lang)
108
- notes: list[str] = []
123
+ notes: list[dict] = []
109
124
  seen: set[str] = set()
110
- for pattern, note in _RUNTIME_SIGNALS:
125
+ for pattern, note, epistemic_level in _RUNTIME_SIGNALS:
111
126
  if note not in seen and pattern.search(clean):
112
- notes.append(note)
127
+ notes.append({"note": note, "epistemic_level": epistemic_level})
113
128
  seen.add(note)
114
129
  return notes
115
130
 
@@ -348,8 +363,11 @@ def analyze_execution_paths(
348
363
  "entry_point": {"step": entry_point_str, "notes": entry_notes},
349
364
  "path": path_items,
350
365
  "end_state": _detect_end_state([item["step"] for item in path_items]),
366
+ "end_state_epistemic_level": "INFERRED (LOW CONFIDENCE)",
351
367
  })
352
368
 
369
+ return result
370
+
353
371
 
354
372
  # ── Behavioral impact helpers ─────────────────────────────────────────────────
355
373
 
@@ -363,9 +381,13 @@ def _domain_from_class(class_name: str) -> str:
363
381
  return re.sub(r"(?<=[a-z])(?=[A-Z])", " ", stripped).strip().lower()
364
382
 
365
383
 
366
- def _impact_item(statement: str, support: str, certainty: str) -> dict:
367
- truth_level = "observed" if certainty == "high" else "inferred"
368
- return {"statement": statement, "support": support, "certainty": certainty, "truth_level": truth_level}
384
+ def _impact_item(statement: str, support: str, certainty: str, *, epistemic_level: Optional[str] = None) -> dict:
385
+ if epistemic_level is None:
386
+ if certainty in ("high", "medium"):
387
+ epistemic_level = "STRUCTURAL SIGNAL"
388
+ else:
389
+ epistemic_level = "INFERRED (LOW CONFIDENCE)"
390
+ return {"statement": statement, "support": support, "epistemic_level": epistemic_level}
369
391
 
370
392
 
371
393
  def _impact_descriptions(
@@ -449,6 +471,7 @@ def _impact_descriptions_for_controller(
449
471
  "controller entry point modified",
450
472
  "controller entry point is in changed files (direct diff evidence)",
451
473
  certainty,
474
+ epistemic_level="FACT",
452
475
  ))
453
476
 
454
477
  if re.search(r"@PreAuthorize|@Secured|@RolesAllowed|hasRole\(|isAuthenticated", ctrl_clean, re.IGNORECASE):
@@ -541,6 +564,7 @@ def analyze_behavioral_impact(
541
564
  "affected_path": affected_path,
542
565
  "impact": _impact_descriptions_for_controller(affected_path, end_state, ctrl_clean, evidence_level),
543
566
  "end_state": end_state,
567
+ "end_state_epistemic_level": "INFERRED (LOW CONFIDENCE)",
544
568
  "confidence": confidence,
545
569
  "evidence_level": evidence_level,
546
570
  "trace": trace,
@@ -638,6 +662,7 @@ def analyze_behavioral_impact(
638
662
  "affected_path": affected_path,
639
663
  "impact": _impact_descriptions(changed_class, changed_type, end_state, ctrl_clean, evidence_level),
640
664
  "end_state": end_state,
665
+ "end_state_epistemic_level": "INFERRED (LOW CONFIDENCE)",
641
666
  "confidence": confidence,
642
667
  "evidence_level": evidence_level,
643
668
  "trace": trace,