sourcecode 1.30.19__tar.gz → 1.30.21__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 (195) hide show
  1. {sourcecode-1.30.19 → sourcecode-1.30.21}/PKG-INFO +3 -3
  2. {sourcecode-1.30.19 → sourcecode-1.30.21}/README.md +2 -2
  3. {sourcecode-1.30.19 → sourcecode-1.30.21}/pyproject.toml +1 -1
  4. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/__init__.py +1 -1
  5. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/cli.py +109 -10
  6. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/prepare_context.py +11 -11
  7. sourcecode-1.30.21/tests/test_output_ux.py +235 -0
  8. {sourcecode-1.30.19 → sourcecode-1.30.21}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  9. {sourcecode-1.30.19 → sourcecode-1.30.21}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  10. {sourcecode-1.30.19 → sourcecode-1.30.21}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  11. {sourcecode-1.30.19 → sourcecode-1.30.21}/.continue-here.md +0 -0
  12. {sourcecode-1.30.19 → sourcecode-1.30.21}/.github/workflows/build-windows.yml +0 -0
  13. {sourcecode-1.30.19 → sourcecode-1.30.21}/.gitignore +0 -0
  14. {sourcecode-1.30.19 → sourcecode-1.30.21}/.ruff.toml +0 -0
  15. {sourcecode-1.30.19 → sourcecode-1.30.21}/CONTRIBUTING.md +0 -0
  16. {sourcecode-1.30.19 → sourcecode-1.30.21}/LICENSE +0 -0
  17. {sourcecode-1.30.19 → sourcecode-1.30.21}/SECURITY.md +0 -0
  18. {sourcecode-1.30.19 → sourcecode-1.30.21}/docs/privacy.md +0 -0
  19. {sourcecode-1.30.19 → sourcecode-1.30.21}/docs/schema.md +0 -0
  20. {sourcecode-1.30.19 → sourcecode-1.30.21}/raw +0 -0
  21. {sourcecode-1.30.19 → sourcecode-1.30.21}/run_cli.py +0 -0
  22. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/adaptive_scanner.py +0 -0
  23. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/architecture_analyzer.py +0 -0
  24. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/architecture_summary.py +0 -0
  25. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/ast_extractor.py +0 -0
  26. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/classifier.py +0 -0
  27. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/code_notes_analyzer.py +0 -0
  28. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/confidence_analyzer.py +0 -0
  29. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/context_scorer.py +0 -0
  30. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/context_summarizer.py +0 -0
  31. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/contract_model.py +0 -0
  32. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/contract_pipeline.py +0 -0
  33. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/coverage_parser.py +0 -0
  34. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/dependency_analyzer.py +0 -0
  35. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/__init__.py +0 -0
  36. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/base.py +0 -0
  37. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/csproj_parser.py +0 -0
  38. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/dart.py +0 -0
  39. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/dotnet.py +0 -0
  40. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/elixir.py +0 -0
  41. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/go.py +0 -0
  42. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/heuristic.py +0 -0
  43. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/hybrid.py +0 -0
  44. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/java.py +0 -0
  45. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/jvm_ext.py +0 -0
  46. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/nodejs.py +0 -0
  47. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/parsers.py +0 -0
  48. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/php.py +0 -0
  49. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/project.py +0 -0
  50. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/python.py +0 -0
  51. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/ruby.py +0 -0
  52. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/rust.py +0 -0
  53. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/systems.py +0 -0
  54. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/terraform.py +0 -0
  55. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/detectors/tooling.py +0 -0
  56. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/doc_analyzer.py +0 -0
  57. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/entrypoint_classifier.py +0 -0
  58. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/env_analyzer.py +0 -0
  59. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/file_classifier.py +0 -0
  60. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/flow_analyzer.py +0 -0
  61. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/git_analyzer.py +0 -0
  62. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/graph_analyzer.py +0 -0
  63. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/metrics_analyzer.py +0 -0
  64. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/pr_comment_renderer.py +0 -0
  65. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/progress.py +0 -0
  66. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/ranking_engine.py +0 -0
  67. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/redactor.py +0 -0
  68. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/relevance_scorer.py +0 -0
  69. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/repo_classifier.py +0 -0
  70. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/repository_ir.py +0 -0
  71. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/runtime_classifier.py +0 -0
  72. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/scanner.py +0 -0
  73. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/schema.py +0 -0
  74. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/semantic_analyzer.py +0 -0
  75. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/serializer.py +0 -0
  76. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/summarizer.py +0 -0
  77. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/telemetry/__init__.py +0 -0
  78. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/telemetry/config.py +0 -0
  79. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/telemetry/consent.py +0 -0
  80. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/telemetry/events.py +0 -0
  81. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/telemetry/filters.py +0 -0
  82. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/telemetry/transport.py +0 -0
  83. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/tree_utils.py +0 -0
  84. {sourcecode-1.30.19 → sourcecode-1.30.21}/src/sourcecode/workspace.py +0 -0
  85. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/__init__.py +0 -0
  86. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/conftest.py +0 -0
  87. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/coverage.xml +0 -0
  88. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  89. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/fastapi_app/src/main.py +0 -0
  90. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  91. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/go_service/go.mod +0 -0
  92. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/jacoco.xml +0 -0
  93. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/latin1_sample.java +0 -0
  94. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/latin1_sample_iso.java +0 -0
  95. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/lcov.info +0 -0
  96. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  97. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/nextjs_app/package.json +0 -0
  98. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  99. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  100. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  101. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  102. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  103. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  104. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  105. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  106. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  107. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  108. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  109. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  110. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  111. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  112. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  113. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  114. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  115. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  116. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  117. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  118. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  119. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  120. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  121. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  122. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  123. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  124. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  125. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  126. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  127. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  128. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  129. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  130. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  131. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_architecture_analyzer.py +0 -0
  132. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_architecture_summary.py +0 -0
  133. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_ast_extractor.py +0 -0
  134. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_block1_reliability.py +0 -0
  135. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_block2_coverage.py +0 -0
  136. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_block5_quality.py +0 -0
  137. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_bug_fixes_v16.py +0 -0
  138. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_classifier.py +0 -0
  139. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_cli.py +0 -0
  140. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_code_notes_analyzer.py +0 -0
  141. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_context_scorer.py +0 -0
  142. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_contract_pipeline.py +0 -0
  143. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_coverage_parser.py +0 -0
  144. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_cross_consistency.py +0 -0
  145. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_dependency_analyzer_node_python.py +0 -0
  146. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_dependency_analyzer_polyglot.py +0 -0
  147. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_dependency_schema.py +0 -0
  148. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_dotnet.py +0 -0
  149. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_go_rust_java.py +0 -0
  150. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_nodejs.py +0 -0
  151. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_php_ruby_dart.py +0 -0
  152. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_python.py +0 -0
  153. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_universal_managed.py +0 -0
  154. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detector_universal_systems.py +0 -0
  155. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_detectors_base.py +0 -0
  156. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_doc_analyzer_jsdom.py +0 -0
  157. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_doc_analyzer_python.py +0 -0
  158. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_encoding_regression.py +0 -0
  159. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_graph_analyzer_polyglot.py +0 -0
  160. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_graph_analyzer_python_node.py +0 -0
  161. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_graph_schema.py +0 -0
  162. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_hybrid_inference.py +0 -0
  163. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration.py +0 -0
  164. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_dependencies.py +0 -0
  165. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_detection.py +0 -0
  166. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_docs.py +0 -0
  167. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_graph_modules.py +0 -0
  168. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_lqn.py +0 -0
  169. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_metrics.py +0 -0
  170. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_multistack.py +0 -0
  171. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_semantics.py +0 -0
  172. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_integration_universal.py +0 -0
  173. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_java_spring_integration.py +0 -0
  174. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_metrics_analyzer.py +0 -0
  175. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_packaging.py +0 -0
  176. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_phase1_improvements.py +0 -0
  177. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_pipeline_integrity.py +0 -0
  178. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_real_projects.py +0 -0
  179. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_redactor.py +0 -0
  180. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_repository_ir.py +0 -0
  181. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_scanner.py +0 -0
  182. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_schema.py +0 -0
  183. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_schema_normalization.py +0 -0
  184. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_semantic_analyzer_node.py +0 -0
  185. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_semantic_analyzer_python.py +0 -0
  186. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_semantic_import_resolution.py +0 -0
  187. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_semantic_schema.py +0 -0
  188. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_signal_hierarchy.py +0 -0
  189. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_summarizer.py +0 -0
  190. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_surface_honesty.py +0 -0
  191. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_task_differentiation.py +0 -0
  192. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_telemetry.py +0 -0
  193. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_v131_improvements.py +0 -0
  194. {sourcecode-1.30.19 → sourcecode-1.30.21}/tests/test_v1_10_regressions.py +0 -0
  195. {sourcecode-1.30.19 → sourcecode-1.30.21}/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.19
3
+ Version: 1.30.21
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.19-blue)
224
+ ![Version](https://img.shields.io/badge/version-1.30.21-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.19
260
+ # sourcecode 1.30.21
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.19-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.30.21-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.19
41
+ # sourcecode 1.30.21
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.19"
7
+ version = "1.30.21"
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.19"
3
+ __version__ = "1.30.21"
@@ -1596,6 +1596,87 @@ def main(
1596
1596
  typer.echo("✓ copied to clipboard", err=True)
1597
1597
 
1598
1598
 
1599
+ # ── prepare-context output helpers ────────────────────────────────────────────
1600
+
1601
+ def _make_explanation(reason: str, why: str) -> str:
1602
+ """Merge reason+why into one human string. Drop internal pipe-format jargon."""
1603
+ if why and not why.startswith("artifact_type:"):
1604
+ return why
1605
+ return reason
1606
+
1607
+
1608
+ def _serialize_relevant_file(f: Any) -> dict:
1609
+ from dataclasses import asdict as _asdict
1610
+ d = {k: v for k, v in _asdict(f).items() if v != "" and v is not None}
1611
+ reason = d.pop("reason", "") or ""
1612
+ why = d.pop("why", "") or ""
1613
+ explanation = _make_explanation(reason, why)
1614
+ if explanation:
1615
+ d["explanation"] = explanation
1616
+ if reason:
1617
+ d["reason"] = reason
1618
+ return d
1619
+
1620
+
1621
+ def _transform_impact_scores(scores: dict) -> dict:
1622
+ """Convert internal impact_score_per_file to human-readable review signals."""
1623
+ _THRESHOLDS = [(0.60, "high"), (0.40, "medium")]
1624
+ _CT_LABELS: dict[str, str] = {
1625
+ "security_change": "security-sensitive change",
1626
+ "behavioral_change": "behavioral change",
1627
+ "structural_change": "structural modification",
1628
+ "configuration_change": "configuration change",
1629
+ "dependency_change": "dependency update",
1630
+ "ui_change": "UI change",
1631
+ }
1632
+ result: dict = {}
1633
+ for path, entry in scores.items():
1634
+ raw = entry.get("_rank_score", 0.0)
1635
+ priority = next((p for thresh, p in _THRESHOLDS if raw >= thresh), "low")
1636
+ signals: list[str] = [
1637
+ _CT_LABELS[ct] for ct in entry.get("change_types", []) if ct in _CT_LABELS
1638
+ ]
1639
+ ev = entry.get("evidence", {})
1640
+ callers = ev.get("reverse_edge_count", 0)
1641
+ if callers:
1642
+ signals.append(f"imported by {callers} other file{'s' if callers > 1 else ''}")
1643
+ out_entry: dict = {"review_priority": priority}
1644
+ if signals:
1645
+ out_entry["signals"] = signals
1646
+ result[path] = out_entry
1647
+ return result
1648
+
1649
+
1650
+ def _clean_system_impact(si: dict) -> dict:
1651
+ """Remove empty lists/dicts from system_impact — only emit positive signals."""
1652
+ return {k: v for k, v in si.items() if v}
1653
+
1654
+
1655
+ def _build_human_summary(output: Any) -> dict:
1656
+ """Human-readable summary block for delta/review-pr output."""
1657
+ concerns: list[str] = []
1658
+ si = getattr(output, "system_impact", {}) or {}
1659
+ for ri in si.get("runtime_impact", []):
1660
+ sig = ri.get("signal", "")
1661
+ if sig:
1662
+ concerns.append(sig)
1663
+ for bc in si.get("behavioral_changes", []):
1664
+ stmt = bc.get("statement", "")
1665
+ if stmt:
1666
+ concerns.append(stmt)
1667
+ cov = getattr(output, "test_coverage_risk", {}) or {}
1668
+ missing = cov.get("changed_files_without_tests", [])
1669
+ if missing:
1670
+ concerns.append(f"{len(missing)} changed file(s) without test coverage detected")
1671
+ summary: dict = {}
1672
+ if getattr(output, "impact_summary", None):
1673
+ summary["description"] = output.impact_summary
1674
+ if concerns:
1675
+ summary["review_concerns"] = concerns[:6]
1676
+ summary["confidence"] = (getattr(output, "confidence", None) or "medium").upper()
1677
+ return summary
1678
+
1679
+
1599
1680
  @app.command("prepare-context")
1600
1681
  def prepare_context_cmd(
1601
1682
  task: Optional[str] = typer.Argument(
@@ -1723,8 +1804,16 @@ def prepare_context_cmd(
1723
1804
  import time as _time
1724
1805
 
1725
1806
  builder = TaskContextBuilder(target)
1807
+ _progress = Progress()
1808
+ _phase = f"analyzing ({task})"
1809
+ if since:
1810
+ _phase += f" since {since}"
1811
+ _progress.start(_phase)
1726
1812
  _t0 = _time.perf_counter()
1727
- output = builder.build(task, since=since, symptom=symptom)
1813
+ try:
1814
+ output = builder.build(task, since=since, symptom=symptom)
1815
+ finally:
1816
+ _progress.finish()
1728
1817
  _t_total = (_time.perf_counter() - _t0) * 1000
1729
1818
 
1730
1819
  if debug_perf:
@@ -1805,7 +1894,7 @@ def prepare_context_cmd(
1805
1894
  out["confidence"] = output.confidence
1806
1895
  if task != "review-pr" and _task_include("relevant_files"):
1807
1896
  out["relevant_files"] = [
1808
- {k: v for k, v in asdict(f).items() if v != ""}
1897
+ _serialize_relevant_file(f)
1809
1898
  for f in output.relevant_files
1810
1899
  ]
1811
1900
  if _task_include("key_dependencies") and output.key_dependencies:
@@ -1856,16 +1945,19 @@ def prepare_context_cmd(
1856
1945
  out["affected_modules"] = output.affected_modules
1857
1946
  if output.risk_areas:
1858
1947
  out["risk_areas"] = output.risk_areas
1859
- if output.why_these_files:
1860
- out["reasoning"] = output.why_these_files
1861
1948
  if output.change_type:
1862
1949
  out["change_type"] = output.change_type
1863
1950
  if output.system_impact:
1864
- out["system_impact"] = output.system_impact
1951
+ _si = _clean_system_impact(output.system_impact)
1952
+ if _si:
1953
+ out["system_impact"] = _si
1865
1954
  if output.dependency_graph_summary:
1866
- out["dependency_graph_summary"] = output.dependency_graph_summary
1955
+ _dgraph = {k: v for k, v in output.dependency_graph_summary.items() if v is not None}
1956
+ if _dgraph:
1957
+ out["dependency_graph_summary"] = _dgraph
1867
1958
  if output.impact_score_per_file:
1868
- out["impact_score_per_file"] = output.impact_score_per_file
1959
+ out["impact_score_per_file"] = _transform_impact_scores(output.impact_score_per_file)
1960
+ out["summary"] = _build_human_summary(output)
1869
1961
  # review-pr specific fields
1870
1962
  if task == "review-pr":
1871
1963
  if output.error_code:
@@ -1920,8 +2012,7 @@ def prepare_context_cmd(
1920
2012
  out["behavioral_impact"] = output.behavioral_impact
1921
2013
  if output.impact_summary:
1922
2014
  out["impact_summary"] = output.impact_summary
1923
- if output.why_these_files:
1924
- out["reasoning"] = output.why_these_files
2015
+ out["summary"] = _build_human_summary(output)
1925
2016
  # git-first scope metadata
1926
2017
  out["scope"] = {
1927
2018
  "source": output.scope_source or "git_diff",
@@ -2105,7 +2196,15 @@ def repo_ir_cmd(
2105
2196
  )
2106
2197
  return
2107
2198
 
2108
- ir = build_repo_ir(file_list, root, since=since)
2199
+ _ir_phase = f"extracting IR ({len(file_list)} files)"
2200
+ if since:
2201
+ _ir_phase += f" since {since}"
2202
+ _ir_progress = Progress()
2203
+ _ir_progress.start(_ir_phase)
2204
+ try:
2205
+ ir = build_repo_ir(file_list, root, since=since)
2206
+ finally:
2207
+ _ir_progress.finish()
2109
2208
  ir = apply_ir_size_limits(
2110
2209
  ir,
2111
2210
  max_nodes=max_nodes,
@@ -556,7 +556,7 @@ _ARTIFACT_CHANGE_EFFECT: dict[str, str] = {
556
556
  "test": "test file (no production code modified)",
557
557
  "documentation": "documentation file (no runtime impact)",
558
558
  "ide_noise": "IDE/tooling artifact (no application impact)",
559
- "source": "application source (artifact role requires annotation inspection)",
559
+ "source": "application source file (role could not be confirmed from code signals)",
560
560
  }
561
561
 
562
562
  # Maps frontend symptom keywords → backend terms likely to contain the root cause.
@@ -1156,22 +1156,24 @@ class TaskContextBuilder:
1156
1156
  else "unknown"
1157
1157
  )
1158
1158
  _impact_entry = _delta_impact_score_per_file.get(_f) or {}
1159
- _entry = {
1159
+ _ev_complete = {k: v for k, v in _impact_entry.get("evidence", {}).items() if v}
1160
+ _entry: dict = {
1160
1161
  "path": _f,
1161
1162
  "diff_source": _diff_source,
1162
1163
  "role": _role_obj,
1163
1164
  "artifact_type": _f_atype,
1164
1165
  "evidence": _evidence,
1165
1166
  "change_effect": {
1166
- "statement": _ARTIFACT_CHANGE_EFFECT.get(_f_atype, "application source (artifact role requires annotation inspection)"),
1167
+ "statement": _ARTIFACT_CHANGE_EFFECT.get(_f_atype, "application source file (role could not be confirmed)"),
1167
1168
  "classification_method": _f_cls.get("confidence", "low"),
1168
1169
  "epistemic_level": (
1169
1170
  "STRUCTURAL SIGNAL" if _f_cls.get("confidence", "low") == "high"
1170
1171
  else "INFERRED (LOW CONFIDENCE)"
1171
1172
  ),
1172
1173
  },
1173
- "evidence_completeness": _impact_entry.get("evidence", {}),
1174
1174
  }
1175
+ if _ev_complete:
1176
+ _entry["evidence_completeness"] = _ev_complete
1175
1177
  _pr_runtime_changes.append(_entry)
1176
1178
  # Split committed vs uncommitted — never merged
1177
1179
  if _f in _committed_set:
@@ -2992,12 +2994,12 @@ class TaskContextBuilder:
2992
2994
 
2993
2995
  # ── Step 6: analysis gaps ──────────────────────────────────────────────
2994
2996
  _bfs_note = (
2995
- f"BFS multi-hop import propagation repo-wide (propagation_depth={_max_hop})"
2997
+ f"multi-hop import tracing performed (depth {_max_hop})"
2996
2998
  if _max_hop >= 1
2997
- else "no import-link propagation (no scannable changed files)"
2999
+ else "no downstream dependencies could be verified"
2998
3000
  )
2999
3001
  analysis_gaps: list[str] = [
3000
- f"Related file expansion: type-aware chain expansion + {_bfs_note} + module/directory heuristics",
3002
+ f"Related file expansion via import analysis and directory proximity ({_bfs_note})",
3001
3003
  ]
3002
3004
  if _expansion_truncated:
3003
3005
  analysis_gaps.insert(0,
@@ -3015,10 +3017,8 @@ class TaskContextBuilder:
3015
3017
  low_confidence = [f for f, cls in classifications.items() if cls.get("confidence") == "low" and not cls["is_noise"]]
3016
3018
  if low_confidence:
3017
3019
  analysis_gaps.append(
3018
- f"{len(low_confidence)} file(s) classified with low confidence"
3019
- " (artifact type inferred from extension only)"
3020
- " — consider adding stem patterns to _classify_changed_file: "
3021
- + ", ".join(Path(f).name for f in low_confidence[:3])
3020
+ f"{len(low_confidence)} file(s) role inferred from filename only"
3021
+ f" (no code signal found): {', '.join(Path(f).name for f in low_confidence[:3])}"
3022
3022
  )
3023
3023
  if not affected_modules_set and any(not cls["is_noise"] for cls in classifications.values()):
3024
3024
  analysis_gaps.append(
@@ -0,0 +1,235 @@
1
+ """test_output_ux.py — Verify output UX: no internal jargon, no redundancy, correct structure.
2
+
3
+ Acceptance criteria (from spec):
4
+ 1. impact_score_per_file: no _rank_score, no has_* booleans
5
+ 2. relevant_files: no 'why' key; 'explanation' present when meaningful
6
+ 3. reasoning block absent from delta/review-pr output
7
+ 4. system_impact: no empty arrays
8
+ 5. human summary (summary) present in delta/review-pr success output
9
+ 6. analysis_gaps: no pipeline jargon ("BFS", "type-aware chain", "import-link propagation")
10
+ 7. Output size ≥ 35% smaller than internal-dump baseline for impact_score_per_file
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ import pytest
19
+ from typer.testing import CliRunner
20
+
21
+ from sourcecode.cli import app
22
+ from sourcecode import prepare_context as _pc
23
+
24
+ runner = CliRunner()
25
+
26
+ FIXTURE = Path(__file__).parent / "fixtures" / "fastapi_app"
27
+
28
+
29
+ def _invoke(*args: str) -> Any:
30
+ return runner.invoke(app, list(args))
31
+
32
+
33
+ def _json(result: Any) -> dict:
34
+ return json.loads(result.output)
35
+
36
+
37
+ # ── helpers ───────────────────────────────────────────────────────────────────
38
+
39
+ def _make_delta_output(monkeypatch, changed: list[str]) -> dict:
40
+ """Return the parsed JSON output for a delta run with mocked changed files."""
41
+ monkeypatch.setattr(
42
+ _pc.TaskContextBuilder,
43
+ "_resolve_git_root",
44
+ lambda self: FIXTURE,
45
+ )
46
+ result = _invoke("prepare-context", "delta", str(FIXTURE), "--since", "HEAD~1")
47
+ assert result.exit_code in (0, 1), result.output
48
+ return _json(result)
49
+
50
+
51
+ def _make_review_pr_output(monkeypatch) -> dict:
52
+ """Return parsed JSON output for review-pr with a mocked changed controller."""
53
+ changed = ["src/main/java/com/example/UserController.java"]
54
+ monkeypatch.setattr(
55
+ _pc.TaskContextBuilder,
56
+ "_resolve_git_root",
57
+ lambda self: FIXTURE,
58
+ )
59
+ monkeypatch.setattr(
60
+ _pc.TaskContextBuilder,
61
+ "_get_pr_scope_files",
62
+ lambda self, since=None: (changed, "git_diff", changed, []),
63
+ )
64
+ result = _invoke("prepare-context", "review-pr", str(FIXTURE))
65
+ assert result.exit_code == 0, result.output
66
+ return _json(result)
67
+
68
+
69
+ # ── impact_score_per_file structure ──────────────────────────────────────────
70
+
71
+ class TestImpactScoreOutput:
72
+ """impact_score_per_file must expose review_priority+signals, not internal scores."""
73
+
74
+ def test_no_rank_score_in_output(self, monkeypatch):
75
+ data = _make_review_pr_output(monkeypatch)
76
+ for entry in data.get("impact_score_per_file", {}).values():
77
+ assert "_rank_score" not in entry, f"_rank_score leaked into output: {entry}"
78
+
79
+ def test_no_boolean_evidence_spam(self, monkeypatch):
80
+ data = _make_review_pr_output(monkeypatch)
81
+ for path, entry in data.get("impact_score_per_file", {}).items():
82
+ ev = entry.get("evidence", {})
83
+ for k, v in ev.items():
84
+ assert v is not False, f"{path}: false evidence key '{k}' should be omitted"
85
+ assert "has_reverse_edges" not in entry, f"boolean key leaked: {path}"
86
+ assert "has_route_diff" not in entry, f"boolean key leaked: {path}"
87
+ assert "has_security_diff" not in entry, f"boolean key leaked: {path}"
88
+ assert "has_wiring_evidence" not in entry, f"boolean key leaked: {path}"
89
+
90
+ def test_review_priority_field_present(self, monkeypatch):
91
+ data = _make_review_pr_output(monkeypatch)
92
+ for path, entry in data.get("impact_score_per_file", {}).items():
93
+ assert "review_priority" in entry, f"review_priority missing for {path}"
94
+ assert entry["review_priority"] in ("high", "medium", "low"), (
95
+ f"unexpected priority value for {path}: {entry['review_priority']}"
96
+ )
97
+
98
+ def test_size_reduction_vs_internal(self, monkeypatch):
99
+ """Transformed impact_score_per_file must be ≥35% smaller in bytes."""
100
+ from sourcecode.cli import _transform_impact_scores
101
+ internal = {
102
+ "src/Foo.java": {
103
+ "_rank_score": 0.325,
104
+ "change_types": ["behavioral_change"],
105
+ "diff_severity": "api_change",
106
+ "evidence": {
107
+ "has_reverse_edges": False,
108
+ "reverse_edge_count": 0,
109
+ "has_route_diff": True,
110
+ "has_security_diff": False,
111
+ "has_wiring_evidence": False,
112
+ },
113
+ }
114
+ }
115
+ before = len(json.dumps(internal))
116
+ after = len(json.dumps(_transform_impact_scores(internal)))
117
+ reduction = (before - after) / before
118
+ assert reduction >= 0.35, (
119
+ f"Expected ≥35% size reduction, got {reduction:.0%}. "
120
+ f"before={before}B after={after}B"
121
+ )
122
+
123
+
124
+ # ── relevant_files structure ──────────────────────────────────────────────────
125
+
126
+ class TestRelevantFilesOutput:
127
+ """relevant_files must not expose 'why' jargon field; explanation replaces it."""
128
+
129
+ def test_no_why_field_in_output(self):
130
+ result = _invoke("prepare-context", "explain", str(FIXTURE))
131
+ assert result.exit_code == 0, result.output
132
+ data = _json(result)
133
+ for rf in data.get("relevant_files", []):
134
+ assert "why" not in rf, (
135
+ f"'why' field leaked into output for {rf.get('path')}: {rf}"
136
+ )
137
+
138
+ def test_explanation_or_reason_present(self):
139
+ result = _invoke("prepare-context", "explain", str(FIXTURE))
140
+ assert result.exit_code == 0, result.output
141
+ data = _json(result)
142
+ for rf in data.get("relevant_files", []):
143
+ has_info = "explanation" in rf or "reason" in rf
144
+ assert has_info, f"No explanation/reason in relevant_file: {rf}"
145
+
146
+
147
+ # ── reasoning block removed ───────────────────────────────────────────────────
148
+
149
+ class TestReasoningBlockRemoved:
150
+ """delta and review-pr must not emit 'reasoning' block (redundant with relevant_files)."""
151
+
152
+ def test_delta_no_reasoning_key(self):
153
+ result = _invoke("prepare-context", "delta", str(FIXTURE))
154
+ assert result.exit_code in (0, 1), result.output
155
+ data = _json(result)
156
+ if data.get("task") == "delta":
157
+ assert "reasoning" not in data, (
158
+ "'reasoning' block still in delta output — should be removed (duplicate)"
159
+ )
160
+
161
+ def test_review_pr_no_reasoning_key(self, monkeypatch):
162
+ data = _make_review_pr_output(monkeypatch)
163
+ assert "reasoning" not in data, (
164
+ "'reasoning' block still in review-pr output — should be removed (duplicate)"
165
+ )
166
+
167
+
168
+ # ── system_impact clean ───────────────────────────────────────────────────────
169
+
170
+ class TestSystemImpactClean:
171
+ """system_impact must not contain empty arrays."""
172
+
173
+ def test_no_empty_arrays_in_system_impact(self):
174
+ result = _invoke("prepare-context", "delta", str(FIXTURE))
175
+ assert result.exit_code in (0, 1), result.output
176
+ data = _json(result)
177
+ si = data.get("system_impact", {})
178
+ for k, v in si.items():
179
+ assert v != [], f"system_impact['{k}'] is empty list — should be omitted"
180
+ assert v != {}, f"system_impact['{k}'] is empty dict — should be omitted"
181
+
182
+
183
+ # ── human summary present ─────────────────────────────────────────────────────
184
+
185
+ class TestHumanSummary:
186
+ """delta and review-pr success output must include a 'summary' block."""
187
+
188
+ def test_review_pr_has_summary(self, monkeypatch):
189
+ data = _make_review_pr_output(monkeypatch)
190
+ assert "summary" in data, "review-pr output missing 'summary' block"
191
+ s = data["summary"]
192
+ assert "confidence" in s, f"summary missing confidence field: {s}"
193
+ assert s["confidence"] in ("HIGH", "MEDIUM", "LOW"), (
194
+ f"confidence has unexpected value: {s['confidence']}"
195
+ )
196
+
197
+
198
+ # ── analysis_gaps language ────────────────────────────────────────────────────
199
+
200
+ class TestAnalysisGapsLanguage:
201
+ """analysis_gaps must not contain pipeline-internal jargon."""
202
+
203
+ _JARGON = [
204
+ "BFS",
205
+ "type-aware chain expansion",
206
+ "import-link propagation",
207
+ "propagation_depth=",
208
+ "artifact role requires annotation inspection",
209
+ ]
210
+
211
+ def test_no_jargon_in_analysis_gaps(self):
212
+ result = _invoke("prepare-context", "delta", str(FIXTURE))
213
+ assert result.exit_code in (0, 1), result.output
214
+ data = _json(result)
215
+ for gap in data.get("gaps", []):
216
+ for jargon in self._JARGON:
217
+ assert jargon not in gap, (
218
+ f"Internal jargon '{jargon}' found in analysis_gaps: {gap!r}"
219
+ )
220
+
221
+
222
+ # ── dependency_graph_summary clean ───────────────────────────────────────────
223
+
224
+ class TestDependencyGraphSummaryClean:
225
+ """dependency_graph_summary must not emit null fields."""
226
+
227
+ def test_no_null_propagation_depth(self):
228
+ result = _invoke("prepare-context", "delta", str(FIXTURE))
229
+ assert result.exit_code in (0, 1), result.output
230
+ data = _json(result)
231
+ dgraph = data.get("dependency_graph_summary", {})
232
+ for k, v in dgraph.items():
233
+ assert v is not None, (
234
+ f"dependency_graph_summary['{k}'] is null — should be omitted"
235
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes