sourcecode 1.31.20__tar.gz → 1.31.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 (227) hide show
  1. sourcecode-1.31.21/.continue-here.md +87 -0
  2. {sourcecode-1.31.20 → sourcecode-1.31.21}/PKG-INFO +3 -3
  3. {sourcecode-1.31.20 → sourcecode-1.31.21}/README.md +2 -2
  4. {sourcecode-1.31.20 → sourcecode-1.31.21}/pyproject.toml +1 -1
  5. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/cli.py +14 -1
  7. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/contract_pipeline.py +21 -1
  8. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/server.py +26 -4
  9. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_bug_fixes_v16.py +82 -0
  10. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_mcp_tools.py +47 -15
  11. sourcecode-1.31.20/.continue-here.md +0 -134
  12. {sourcecode-1.31.20 → sourcecode-1.31.21}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  13. {sourcecode-1.31.20 → sourcecode-1.31.21}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  14. {sourcecode-1.31.20 → sourcecode-1.31.21}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  15. {sourcecode-1.31.20 → sourcecode-1.31.21}/.github/workflows/build-windows.yml +0 -0
  16. {sourcecode-1.31.20 → sourcecode-1.31.21}/.gitignore +0 -0
  17. {sourcecode-1.31.20 → sourcecode-1.31.21}/.ruff.toml +0 -0
  18. {sourcecode-1.31.20 → sourcecode-1.31.21}/.sourcecode-cache/snapshot-3b5997a-fa5c742c.json +0 -0
  19. {sourcecode-1.31.20 → sourcecode-1.31.21}/AUDIT_REAL_REPOS.md +0 -0
  20. {sourcecode-1.31.20 → sourcecode-1.31.21}/CHANGELOG.md +0 -0
  21. {sourcecode-1.31.20 → sourcecode-1.31.21}/CONTRIBUTING.md +0 -0
  22. {sourcecode-1.31.20 → sourcecode-1.31.21}/LICENSE +0 -0
  23. {sourcecode-1.31.20 → sourcecode-1.31.21}/SECURITY.md +0 -0
  24. {sourcecode-1.31.20 → sourcecode-1.31.21}/docs/PRODUCT_TIERS.md +0 -0
  25. {sourcecode-1.31.20 → sourcecode-1.31.21}/docs/privacy.md +0 -0
  26. {sourcecode-1.31.20 → sourcecode-1.31.21}/docs/schema.md +0 -0
  27. {sourcecode-1.31.20 → sourcecode-1.31.21}/raw +0 -0
  28. {sourcecode-1.31.20 → sourcecode-1.31.21}/run_cli.py +0 -0
  29. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/adaptive_scanner.py +0 -0
  30. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/architecture_analyzer.py +0 -0
  31. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/architecture_summary.py +0 -0
  32. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/ast_extractor.py +0 -0
  33. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/cache.py +0 -0
  34. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/canonical_ir.py +0 -0
  35. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/classifier.py +0 -0
  36. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/code_notes_analyzer.py +0 -0
  37. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/confidence_analyzer.py +0 -0
  38. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/context_scorer.py +0 -0
  39. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/context_summarizer.py +0 -0
  40. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/contract_model.py +0 -0
  41. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/coverage_parser.py +0 -0
  42. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/dependency_analyzer.py +0 -0
  43. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/__init__.py +0 -0
  44. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/base.py +0 -0
  45. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/csproj_parser.py +0 -0
  46. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/dart.py +0 -0
  47. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/dotnet.py +0 -0
  48. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/elixir.py +0 -0
  49. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/go.py +0 -0
  50. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/heuristic.py +0 -0
  51. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/hybrid.py +0 -0
  52. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/java.py +0 -0
  53. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/jvm_ext.py +0 -0
  54. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/nodejs.py +0 -0
  55. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/parsers.py +0 -0
  56. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/php.py +0 -0
  57. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/project.py +0 -0
  58. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/python.py +0 -0
  59. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/ruby.py +0 -0
  60. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/rust.py +0 -0
  61. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/systems.py +0 -0
  62. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/terraform.py +0 -0
  63. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/detectors/tooling.py +0 -0
  64. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/doc_analyzer.py +0 -0
  65. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/entrypoint_classifier.py +0 -0
  66. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/env_analyzer.py +0 -0
  67. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/file_classifier.py +0 -0
  68. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/flow_analyzer.py +0 -0
  69. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/git_analyzer.py +0 -0
  70. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/graph_analyzer.py +0 -0
  71. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/__init__.py +0 -0
  72. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  73. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  74. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  75. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  76. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  77. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/mcp/runner.py +0 -0
  78. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/metrics_analyzer.py +0 -0
  79. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/output_budget.py +0 -0
  80. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/path_filters.py +0 -0
  81. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/pr_comment_renderer.py +0 -0
  82. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/prepare_context.py +0 -0
  83. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/progress.py +0 -0
  84. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/ranking_engine.py +0 -0
  85. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/redactor.py +0 -0
  86. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/relevance_scorer.py +0 -0
  87. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/repo_classifier.py +0 -0
  88. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/repository_ir.py +0 -0
  89. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/runtime_classifier.py +0 -0
  90. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/scanner.py +0 -0
  91. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/schema.py +0 -0
  92. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/semantic_analyzer.py +0 -0
  93. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/serializer.py +0 -0
  94. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/summarizer.py +0 -0
  95. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/telemetry/__init__.py +0 -0
  96. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/telemetry/config.py +0 -0
  97. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/telemetry/consent.py +0 -0
  98. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/telemetry/events.py +0 -0
  99. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/telemetry/filters.py +0 -0
  100. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/telemetry/transport.py +0 -0
  101. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/tree_utils.py +0 -0
  102. {sourcecode-1.31.20 → sourcecode-1.31.21}/src/sourcecode/workspace.py +0 -0
  103. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/__init__.py +0 -0
  104. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/conftest.py +0 -0
  105. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/coverage.xml +0 -0
  106. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  107. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/fastapi_app/src/main.py +0 -0
  108. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  109. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/go_service/go.mod +0 -0
  110. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/jacoco.xml +0 -0
  111. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/latin1_sample.java +0 -0
  112. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/latin1_sample_iso.java +0 -0
  113. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/lcov.info +0 -0
  114. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  115. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/nextjs_app/package.json +0 -0
  116. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  117. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  118. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  119. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  120. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  121. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  122. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  123. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  124. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  125. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  126. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  127. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  128. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  129. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  130. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  131. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  132. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  133. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  134. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  135. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  136. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  137. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  138. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  139. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  140. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  141. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  142. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  143. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  144. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  145. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  146. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  147. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  148. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  149. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_architecture_analyzer.py +0 -0
  150. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_architecture_summary.py +0 -0
  151. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_ast_extractor.py +0 -0
  152. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_audit_fixes.py +0 -0
  153. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_audit_sas_v2.py +0 -0
  154. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_block1_reliability.py +0 -0
  155. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_block2_coverage.py +0 -0
  156. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_block5_quality.py +0 -0
  157. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_broadleaf_fixes.py +0 -0
  158. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_bug_fixes_v1302.py +0 -0
  159. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_bug_fixes_v13115.py +0 -0
  160. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_bug_fixes_v1312.py +0 -0
  161. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_bug_fixes_v1313.py +0 -0
  162. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_bug_fixes_v2.py +0 -0
  163. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_cache.py +0 -0
  164. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_canonical_ir.py +0 -0
  165. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_classifier.py +0 -0
  166. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_cli.py +0 -0
  167. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_code_notes_analyzer.py +0 -0
  168. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_context_scorer.py +0 -0
  169. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_contract_pipeline.py +0 -0
  170. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_coverage_parser.py +0 -0
  171. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_cross_consistency.py +0 -0
  172. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_dependency_analyzer_node_python.py +0 -0
  173. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_dependency_analyzer_polyglot.py +0 -0
  174. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_dependency_schema.py +0 -0
  175. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_dotnet.py +0 -0
  176. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_go_rust_java.py +0 -0
  177. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_nodejs.py +0 -0
  178. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_php_ruby_dart.py +0 -0
  179. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_python.py +0 -0
  180. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_universal_managed.py +0 -0
  181. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detector_universal_systems.py +0 -0
  182. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_detectors_base.py +0 -0
  183. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_doc_analyzer_jsdom.py +0 -0
  184. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_doc_analyzer_python.py +0 -0
  185. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_encoding_regression.py +0 -0
  186. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_enterprise_benchmarks.py +0 -0
  187. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_graph_analyzer_polyglot.py +0 -0
  188. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_graph_analyzer_python_node.py +0 -0
  189. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_graph_schema.py +0 -0
  190. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_hybrid_inference.py +0 -0
  191. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration.py +0 -0
  192. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_dependencies.py +0 -0
  193. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_detection.py +0 -0
  194. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_docs.py +0 -0
  195. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_graph_modules.py +0 -0
  196. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_lqn.py +0 -0
  197. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_metrics.py +0 -0
  198. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_multistack.py +0 -0
  199. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_semantics.py +0 -0
  200. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_integration_universal.py +0 -0
  201. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_java_spring_integration.py +0 -0
  202. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_mcp_runner.py +0 -0
  203. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_mcp_serve.py +0 -0
  204. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_metrics_analyzer.py +0 -0
  205. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_output_ux.py +0 -0
  206. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_packaging.py +0 -0
  207. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_phase1_improvements.py +0 -0
  208. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_pipeline_integrity.py +0 -0
  209. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_real_projects.py +0 -0
  210. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_redactor.py +0 -0
  211. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_repository_ir.py +0 -0
  212. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_scanner.py +0 -0
  213. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_schema.py +0 -0
  214. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_schema_normalization.py +0 -0
  215. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_scoring_calibration.py +0 -0
  216. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_semantic_analyzer_node.py +0 -0
  217. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_semantic_analyzer_python.py +0 -0
  218. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_semantic_import_resolution.py +0 -0
  219. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_semantic_schema.py +0 -0
  220. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_signal_hierarchy.py +0 -0
  221. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_summarizer.py +0 -0
  222. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_surface_honesty.py +0 -0
  223. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_task_differentiation.py +0 -0
  224. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_telemetry.py +0 -0
  225. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_v131_improvements.py +0 -0
  226. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_v1_10_regressions.py +0 -0
  227. {sourcecode-1.31.20 → sourcecode-1.31.21}/tests/test_workspace_analyzer.py +0 -0
@@ -0,0 +1,87 @@
1
+ # Handoff — sesión 22
2
+ **Date:** 2026-05-25T09:10:25Z
3
+ **Branch:** master
4
+ **Last commit:** `f1e80b6 feat(cache): introduce two-layer cache (L1 core + L2 view)`
5
+ **Suite:** 1510 passed, 3 skipped — all green
6
+
7
+ ---
8
+
9
+ ## What was done this session
10
+
11
+ ### Credibility audit (`AUDIT_REAL_REPOS.md`)
12
+ Full adversarial audit of `sourcecode` against Keycloak (7885 Java files) and BroadleafCommerce (2985 Java files). Findings logged in `AUDIT_REAL_REPOS.md` at repo root. Key bugs fixed in prior sessions (P0-01 Spring DI bridging, P0-02 repo-ir boundedness).
13
+
14
+ ### Two-layer cache architecture (this session)
15
+
16
+ **Problem:** cache key was `(commit, ALL_flags)` → N flag combos × M commits = N×M full snapshots. Same commit with `--compact` vs `--agent` ran the full analysis pipeline twice.
17
+
18
+ **Solution implemented:**
19
+ - **L1 core** `core-<sha>-<analysis_hash>.json.gz` → pre-computed view data, keyed by analysis-affecting flags only
20
+ - **L2 view** `view-<core_hash16>-<view_hash>.json.gz` → rendered string, keyed by view-affecting flags
21
+ - Lookup: L2 hit → L1 hit + view rebuild → full analysis (skip analysis on L1 hit)
22
+
23
+ **Files changed:**
24
+ | File | What |
25
+ |------|------|
26
+ | `src/sourcecode/serializer.py` | Added `core_view(sm)` + `build_view_from_core()` + `CORE_VIEW_VERSION` |
27
+ | `src/sourcecode/cache.py` | Added `read_core/write_core`, `read_view/write_view`, `_gc_views()`, updated `_gc()` |
28
+ | `src/sourcecode/cli.py` | Replaced single-key cache block with 2-layer lookup + write; split flags into core vs view |
29
+ | `tests/test_cache.py` | +12 new tests: TestCoreCache, TestViewCache, TestGCLayered (44 total cache tests) |
30
+
31
+ **Flag split:**
32
+ - Core (analysis): `dep/gm/docs/fm/sem/arch/gc/em/cn/mode/exclude/depth` + version
33
+ - View (presentation): `compact/agent/format/full/no_tree/tree/rank_by/symbol/entrypoints_only/no_redact/graph_detail/docs_depth/max_nodes/graph_edges/max_importers/emit_graph`
34
+
35
+ ---
36
+
37
+ ## Current state
38
+
39
+ Working tree is **clean** — everything committed.
40
+
41
+ GSD state: milestone v1.0, 13 phases all complete (100%). No pending todos in `.planning/STATE.md`.
42
+
43
+ Outstanding work from `AUDIT_REAL_REPOS.md` (NOT yet implemented — only audit findings logged):
44
+
45
+ ### P1 remaining (from audit):
46
+ - **BUG-P1-01** Risk score inconsistency: `OrderServiceImpl` 0 callers → risk_level: low vs `OrderDaoImpl` 0 callers → risk_level: high. Same root cause, different heuristics fire.
47
+ - **BUG-P1-02** `project_summary` copies README blurb instead of generating from code structure
48
+ - **BUG-P1-03** `fix-bug` returns 426 relevant files for generic NPE symptom — no score field, no cap
49
+ - **BUG-P1-04** `indirect_callers: 0` for KeycloakSession (1992 direct callers) — BFS exhausts at level 1
50
+
51
+ ### P2 remaining:
52
+ - BUG-P2-01 `bounded_contexts` detection wrong (uses utility packages, not Maven modules)
53
+ - BUG-P2-02 `role: unknown` for all modernize high_coupling_nodes
54
+ - BUG-P2-03 `no_security_signal` always 100% for filter-based security (JAX-RS/XML)
55
+ - BUG-P2-04 JAX-RS sub-resource paths not composed with parent `@Path`
56
+ - BUG-P2-05 Broadleaf admin paths mixed into REST endpoints
57
+ - BUG-P2-06 Architecture confidence inconsistent between `--compact` and `--agent`
58
+ - BUG-P2-07 `entry_points.controllers.methods: 21` vs endpoints finding 130 (unexplained gap)
59
+ - BUG-P2-08 `--format`/`--no-cache` inconsistently available across subcommands
60
+
61
+ ### Cosmetic:
62
+ - Code notes URLs truncated (`s.webkit.org/...`)
63
+ - `truncated: None` vs `truncated: false` inconsistency
64
+ - `--deep` flag referenced in output but absent from `--help`
65
+ - `--compact --help` claims 1000-3000 tokens; measured 2856-4031
66
+
67
+ ---
68
+
69
+ ## Next session: where to start
70
+
71
+ **Option A** — tackle P1 bugs from audit (highest credibility impact):
72
+ 1. P1-01: risk score consistency when direct_callers=0 (Spring impl detection)
73
+ 2. P1-02: `project_summary` generation from code structure not README
74
+
75
+ **Option B** — write integration tests for 2-layer cache (verify cli.py actually hits L1 on second run with different view flags)
76
+
77
+ **Option C** — version bump + changelog (v1.32.0) with 2-layer cache as headline
78
+
79
+ **Recommended: Option A → P1-01 first** (user-visible correctness, shortest fix).
80
+
81
+ ---
82
+
83
+ ## Resume
84
+
85
+ ```
86
+ cat .continue-here.md
87
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.31.20
3
+ Version: 1.31.21
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -225,7 +225,7 @@ Description-Content-Type: text/markdown
225
225
 
226
226
  **AI-ready change intelligence for Java/Spring enterprise monoliths.**
227
227
 
228
- ![Version](https://img.shields.io/badge/version-1.31.20-blue)
228
+ ![Version](https://img.shields.io/badge/version-1.31.21-blue)
229
229
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
230
230
 
231
231
  ---
@@ -263,7 +263,7 @@ pipx install sourcecode
263
263
 
264
264
  ```bash
265
265
  sourcecode version
266
- # sourcecode 1.31.20
266
+ # sourcecode 1.31.21
267
267
  ```
268
268
 
269
269
  ---
@@ -2,7 +2,7 @@
2
2
 
3
3
  **AI-ready change intelligence for Java/Spring enterprise monoliths.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.31.20-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.31.21-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.10%2B-green)
7
7
 
8
8
  ---
@@ -40,7 +40,7 @@ pipx install sourcecode
40
40
 
41
41
  ```bash
42
42
  sourcecode version
43
- # sourcecode 1.31.20
43
+ # sourcecode 1.31.21
44
44
  ```
45
45
 
46
46
  ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.31.20"
7
+ version = "1.31.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.31.20"
3
+ __version__ = "1.31.21"
@@ -1776,8 +1776,21 @@ def main(
1776
1776
  if changed_only and _allowed_changed_files:
1777
1777
  # GAP-5: preserve full entry_points for architecture context even in
1778
1778
  # --changed-only mode. Only filter file_paths and code_notes.
1779
+ # ALWAYS-INCLUDE: security-const files must stay in file_paths even when
1780
+ # not in the git diff — they resolve Java constant references used in
1781
+ # @M3FiltroSeguridad annotations (read-only anchors, not diff output).
1782
+ def _is_always_include_ref(p: str) -> bool:
1783
+ name = p.rsplit("/", 1)[-1].rsplit("\\", 1)[-1]
1784
+ if name.endswith("Const.java") or name.endswith("Constants.java"):
1785
+ return True
1786
+ parts = p.replace("\\", "/").lower().split("/")
1787
+ return any(seg in ("security", "seguridad", "constantes") for seg in parts)
1788
+
1779
1789
  sm = _replace(sm,
1780
- file_paths=[p for p in sm.file_paths if p in _allowed_changed_files],
1790
+ file_paths=[
1791
+ p for p in sm.file_paths
1792
+ if p in _allowed_changed_files or _is_always_include_ref(p)
1793
+ ],
1781
1794
  code_notes=[n for n in sm.code_notes if n.path in _allowed_changed_files],
1782
1795
  )
1783
1796
  data = compact_view(sm, no_tree=no_tree, full=full)
@@ -242,7 +242,27 @@ class ContractPipeline:
242
242
  ]
243
243
 
244
244
  if changed_only:
245
- src_paths = [p for p in src_paths if p in changed_files]
245
+ def _is_always_include_ref(p: str) -> bool:
246
+ """Always include security-const files regardless of diff status.
247
+
248
+ These are read-only reference anchors: they resolve Java constant
249
+ references (e.g. SeguridadRecursosConst.REC_X) used in security
250
+ annotations. They do NOT appear in diff output because
251
+ contract.is_changed is set from ``changed_files`` only (line below).
252
+ Criteria per bug-spec:
253
+ - name ends with Const.java or Constants.java
254
+ - any path segment is: security | seguridad | constantes
255
+ """
256
+ name = p.rsplit("/", 1)[-1].rsplit("\\", 1)[-1]
257
+ if name.endswith("Const.java") or name.endswith("Constants.java"):
258
+ return True
259
+ parts = p.replace("\\", "/").lower().split("/")
260
+ return any(seg in ("security", "seguridad", "constantes") for seg in parts)
261
+
262
+ src_paths = [
263
+ p for p in src_paths
264
+ if p in changed_files or _is_always_include_ref(p)
265
+ ]
246
266
 
247
267
  # Apply max_files cap — bypass when symbol search to ensure defining files are found.
248
268
  # A symbol query over a large repo needs all files; result set is small after filtering.
@@ -10,10 +10,12 @@ data is the parsed JSON object from the CLI output, not a shell string.
10
10
  """
11
11
  from __future__ import annotations
12
12
 
13
+ import json
13
14
  import os
14
15
  from typing import Any
15
16
 
16
17
  from mcp.server.fastmcp import FastMCP
18
+ from mcp.types import CallToolResult, TextContent
17
19
 
18
20
  from sourcecode import __version__ as _sourcecode_version
19
21
  from sourcecode.mcp.runner import run_command
@@ -30,15 +32,35 @@ def _ok(data: Any) -> dict:
30
32
  return {"success": True, "data": data, "error": None}
31
33
 
32
34
 
33
- def _err(message: str, code: str = "EXECUTION_FAILED") -> dict:
34
- return {"success": False, "data": None, "error": {"code": code, "message": message}}
35
+ def _err(message: str, code: str = "EXECUTION_FAILED") -> CallToolResult:
36
+ """Return an MCP tool-error result with isError=True per MCP spec §tool-result."""
37
+ payload = {"success": False, "data": None, "error": {"code": code, "message": message}}
38
+ return CallToolResult(
39
+ content=[TextContent(type="text", text=json.dumps(payload))],
40
+ isError=True,
41
+ )
35
42
 
36
43
 
37
- def _execute(args: list[str]) -> dict:
44
+ def _execute(args: list[str]) -> dict | CallToolResult:
38
45
  try:
39
- return _ok(run_command(args))
46
+ result = run_command(args)
40
47
  except RuntimeError as exc:
41
48
  return _err(str(exc))
49
+ # If CLI output itself signals failure via success:false, propagate as isError=True
50
+ if isinstance(result, dict) and result.get("success") is False:
51
+ payload = {
52
+ "success": False,
53
+ "data": None,
54
+ "error": result.get("error") or {
55
+ "code": "EXECUTION_FAILED",
56
+ "message": "Command returned success=false",
57
+ },
58
+ }
59
+ return CallToolResult(
60
+ content=[TextContent(type="text", text=json.dumps(payload))],
61
+ isError=True,
62
+ )
63
+ return _ok(result)
42
64
 
43
65
 
44
66
  @mcp.tool()
@@ -248,3 +248,85 @@ def test_fix_bug_why_field_populated(tmp_path: Path) -> None:
248
248
  assert "FIXME" in top.why or "annotation" in top.why, (
249
249
  f"Expected FIXME signal in why, got: {top.why!r}"
250
250
  )
251
+
252
+
253
+ # ---------------------------------------------------------------------------
254
+ # Bug 5 — --changed-only always includes security-const files
255
+ # ---------------------------------------------------------------------------
256
+
257
+ def test_contract_pipeline_changed_only_always_includes_const_files(tmp_path: Path) -> None:
258
+ """--changed-only must include *Const.java files regardless of diff status.
259
+
260
+ These are read-only reference anchors: included in the analysis to resolve
261
+ constant references but not marked as is_changed (not part of diff output).
262
+ """
263
+ from sourcecode.contract_pipeline import ContractPipeline
264
+
265
+ changed = ["src/OrderService.java"]
266
+ const_file = "src/security/SeguridadRecursosConst.java"
267
+ all_files = changed + [const_file]
268
+
269
+ for f in all_files:
270
+ p = tmp_path / f
271
+ p.parent.mkdir(parents=True, exist_ok=True)
272
+ p.write_text("public class Placeholder {}")
273
+
274
+ cp = ContractPipeline(max_files=100)
275
+ contracts, _ = cp.run(
276
+ tmp_path,
277
+ all_files,
278
+ changed_only=True,
279
+ allowed_changed_files=set(changed),
280
+ )
281
+
282
+ contract_paths = {c.path for c in contracts}
283
+ assert const_file in contract_paths, (
284
+ "Const file must be always-included even when absent from diff"
285
+ )
286
+ # Read-only anchor — must NOT be marked as changed
287
+ const_contract = next(c for c in contracts if c.path == const_file)
288
+ assert not const_contract.is_changed, "Const file is read-only anchor: is_changed must be False"
289
+
290
+
291
+ def test_changed_only_resource_names_unresolved_le5(tmp_path: Path) -> None:
292
+ """With --changed-only active, resource_names_unresolved must be <= 5.
293
+
294
+ The always-include logic ensures *Const.java files remain in file_paths for
295
+ constant resolution even when they are absent from the git diff.
296
+ """
297
+ from sourcecode.serializer import _security_surface_from_eps
298
+
299
+ # Create the const file on disk (not in the git diff)
300
+ const_dir = tmp_path / "src" / "security"
301
+ const_dir.mkdir(parents=True)
302
+ (const_dir / "SeguridadRecursosConst.java").write_text(
303
+ "public class SeguridadRecursosConst {\n"
304
+ ' public static final String REC_PEDIDOS = "PEDIDOS";\n'
305
+ ' public static final String REC_USUARIOS = "USUARIOS";\n'
306
+ ' public static final String REC_FACTURAS = "FACTURAS";\n'
307
+ "}\n"
308
+ )
309
+ rel_const = "src/security/SeguridadRecursosConst.java"
310
+
311
+ class _MockEP:
312
+ def __init__(self, evidence: str) -> None:
313
+ self.evidence = evidence
314
+
315
+ eps = [
316
+ _MockEP('nombreRecurso="SeguridadRecursosConst.REC_PEDIDOS"'),
317
+ _MockEP('nombreRecurso="SeguridadRecursosConst.REC_USUARIOS"'),
318
+ _MockEP('nombreRecurso="SeguridadRecursosConst.REC_FACTURAS"'),
319
+ ]
320
+
321
+ # Baseline (--changed-only WITHOUT always-include): const file absent → unresolved
322
+ without = _security_surface_from_eps(eps, root=tmp_path, file_paths=[])
323
+ n_without = len((without or {}).get("resource_names_unresolved", []))
324
+ assert n_without >= 1, "Baseline: absent const file must produce unresolved names"
325
+
326
+ # After fix (always-include): const file in file_paths → resolved
327
+ with_const = _security_surface_from_eps(eps, root=tmp_path, file_paths=[rel_const])
328
+ n_with = len((with_const or {}).get("resource_names_unresolved", []))
329
+ assert n_with <= 5, (
330
+ f"resource_names_unresolved must be <=5 when const file is always-included, got {n_with}"
331
+ )
332
+ assert n_with == 0, f"All 3 constants must resolve, got {n_with} unresolved"
@@ -1,19 +1,23 @@
1
1
  """Contract tests for sourcecode.mcp.server MCP tools.
2
2
 
3
- All tools must return the canonical output contract:
3
+ Success contract (dict returned, FastMCP wraps with isError=False):
4
4
  {"success": bool, "data": dict | str | None, "error": {"code": str, "message": str} | None}
5
5
 
6
- data is the parsed JSON object from the CLI output, not a shell string.
6
+ Failure contract (CallToolResult with isError=True per MCP spec §tool-result):
7
+ CallToolResult.isError == True
8
+ CallToolResult.content[0].text == JSON{"success": false, "data": null, "error": {...}}
7
9
 
8
10
  Requires: pip install sourcecode[mcp]
9
11
  """
10
12
  import json
13
+ from typing import Any
11
14
  from unittest.mock import patch
12
15
 
13
16
  import pytest
14
17
 
15
18
  mcp_pkg = pytest.importorskip("mcp", reason="mcp extra not installed — skip MCP tool tests")
16
19
 
20
+ from mcp.types import CallToolResult, TextContent # noqa: E402
17
21
  from sourcecode.mcp import server # noqa: E402 — after importorskip guard
18
22
 
19
23
  _PARSED_OUTPUT = {"project": {"primary_stack": "python"}}
@@ -30,17 +34,23 @@ def _assert_success(result: dict) -> None:
30
34
  json.dumps(result)
31
35
 
32
36
 
33
- def _assert_failure(result: dict, expected_code: str | None = None) -> None:
34
- assert isinstance(result, dict)
35
- assert set(result.keys()) == _SUCCESS_KEYS, f"unexpected keys: {set(result.keys())}"
36
- assert result["success"] is False
37
- assert result["data"] is None
38
- assert isinstance(result["error"], dict)
39
- assert "code" in result["error"]
40
- assert "message" in result["error"]
37
+ def _assert_failure(result: Any, expected_code: str | None = None) -> None:
38
+ """Assert tool returned CallToolResult with isError=True per MCP spec §tool-result."""
39
+ assert isinstance(result, CallToolResult), (
40
+ f"expected CallToolResult, got {type(result).__name__}"
41
+ )
42
+ assert result.isError is True, "isError must be True for tool failures"
43
+ assert len(result.content) == 1, "failure result must carry exactly one content item"
44
+ text = result.content[0]
45
+ assert isinstance(text, TextContent), f"content[0] must be TextContent, got {type(text).__name__}"
46
+ payload = json.loads(text.text)
47
+ assert payload["success"] is False
48
+ assert payload["data"] is None
49
+ assert isinstance(payload["error"], dict)
50
+ assert "code" in payload["error"]
51
+ assert "message" in payload["error"]
41
52
  if expected_code:
42
- assert result["error"]["code"] == expected_code
43
- json.dumps(result)
53
+ assert payload["error"]["code"] == expected_code
44
54
 
45
55
 
46
56
  # --- get_compact_context ---
@@ -82,7 +92,8 @@ def test_get_compact_context_failure():
82
92
  with patch(_RUNNER_PATH, side_effect=RuntimeError("boom")):
83
93
  result = server.get_compact_context("/some/repo")
84
94
  _assert_failure(result, "EXECUTION_FAILED")
85
- assert "boom" in result["error"]["message"]
95
+ payload = json.loads(result.content[0].text)
96
+ assert "boom" in payload["error"]["message"]
86
97
 
87
98
 
88
99
  # --- get_agent_context ---
@@ -328,11 +339,32 @@ def test_no_extra_keys_on_success():
328
339
 
329
340
 
330
341
  def test_no_extra_keys_on_failure():
342
+ """Runtime failures return CallToolResult with isError=True per MCP spec §tool-result."""
331
343
  with patch(_RUNNER_PATH, side_effect=RuntimeError("x")):
332
344
  r = server.get_compact_context("/p")
333
- assert set(r.keys()) == _SUCCESS_KEYS
345
+ assert isinstance(r, CallToolResult)
346
+ assert r.isError is True
334
347
 
335
348
 
336
349
  def test_no_extra_keys_on_validation_error():
350
+ """Validation failures return CallToolResult with isError=True per MCP spec §tool-result."""
337
351
  r = server.get_module_context("/p", "")
338
- assert set(r.keys()) == _SUCCESS_KEYS
352
+ assert isinstance(r, CallToolResult)
353
+ assert r.isError is True
354
+
355
+
356
+ def test_get_impact_context_nonexistent_class_returns_is_error():
357
+ """Tool call with nonexistent class returns isError=True per MCP spec §tool-result.
358
+
359
+ When the subprocess exits non-zero (class not found), the MCP response must
360
+ carry isError=True so AI agents distinguish errors from successful results.
361
+ """
362
+ with patch(_RUNNER_PATH, side_effect=RuntimeError("class 'NonExistentClass12345' not found")):
363
+ result = server.get_impact_context("/some/repo", target="NonExistentClass12345")
364
+ assert isinstance(result, CallToolResult), (
365
+ f"expected CallToolResult, got {type(result).__name__}"
366
+ )
367
+ assert result.isError is True, "isError must be True when target class does not exist"
368
+ payload = json.loads(result.content[0].text)
369
+ assert payload["success"] is False
370
+ assert "NonExistentClass12345" in payload["error"]["message"]
@@ -1,134 +0,0 @@
1
- # Continue Here — atlas-cli sesión 21
2
-
3
- **Paused:** 2026-05-24
4
- **Repo:** `/Users/user/Documents/workspace/atlas-cli`
5
- **Branch:** master
6
- **Version:** sourcecode 1.31.17
7
-
8
- ---
9
-
10
- ## Objetivo de esta sesión
11
-
12
- Continuar corrección de bugs post-auditoría v1.31.16 (adversarial audit contra Keycloak + Broadleaf).
13
- Sesión 21 atacó los dos P0s. Quedan P1s y P2s.
14
-
15
- ---
16
-
17
- ## Trabajo completado esta sesión
18
-
19
- ### P0-01 — `impact OrderServiceImpl` → 0 callers FIXED ✅
20
-
21
- **Root cause:** `_build_reverse_adjacency` descartaba edges `implements` cuando `to` era
22
- nombre corto no-resuelto (`"OrderService"` en vez de FQN). El `reverse_graph["OrderService"]`
23
- no tenía clave `"implements"` → scan desde reverse side imposible.
24
-
25
- **Fix:** Escanear `graph.edges` forward para `type=implements` FROM matched classes.
26
- Resolver `to` (short/FQN) contra claves de `reverse_graph` via suffix match.
27
- Callers de la interfaz se añaden a `direct_callers`. Output incluye `via_interface_resolution`
28
- y `via_interface_note`.
29
-
30
- **Resultado:** `impact OrderServiceImpl` en Broadleaf: 0 callers → **74 callers**, risk LOW → **HIGH**.
31
-
32
- ### P0-02 — `reverse_graph` unbounded por `--max-nodes`/`--max-edges` FIXED ✅
33
-
34
- **Root cause:** `apply_ir_size_limits` sólo acotaba `graph.nodes`/`graph.edges`.
35
- `reverse_graph` emitía 2685 claves (~3MB) aunque se pidieran 200 nodos.
36
-
37
- **Fix:** Cuando `max_nodes` activo: restringir `reverse_graph` a `kept_fqns` + cap inner
38
- caller lists a `max(20, max_nodes//4)`. Cuando sólo `max_edges`: cap a `max_edges` claves
39
- por in-degree. Añade `reverse_graph_note` cuando trimmed.
40
-
41
- **Resultado:** `--max-nodes 200 --max-edges 500`: 3.85MB → **939KB** (76% reducción).
42
-
43
- ### Commit esta sesión
44
-
45
- ```
46
- e5fba19 fix(ir): resolve Spring DI interface bridging and bound reverse_graph
47
- ```
48
-
49
- ---
50
-
51
- ## Estado archivos sin commitear
52
-
53
- ```
54
- ?? AUDIT_REAL_REPOS.md — auditoría 22 secciones (de sesión 20, no tocar)
55
- ?? docs/PRODUCT_TIERS.md — de sesión anterior (no tocar esta sesión)
56
- ```
57
-
58
- Commitear estos docs antes de continuar con fixes:
59
- ```bash
60
- git add AUDIT_REAL_REPOS.md docs/PRODUCT_TIERS.md
61
- git commit -m "docs(audit): adversarial audit v1.31.16 — real benchmarks, P0/P1/P2 findings"
62
- ```
63
-
64
- ---
65
-
66
- ## Bugs pendientes por ROI
67
-
68
- ### P1 — Altos
69
-
70
- | # | Bug | Archivo | Tiempo est. |
71
- |---|-----|---------|-------------|
72
- | **P1-01** | Risk score inconsistente para 0-caller impls: `OrderServiceImpl`→low vs `OrderDaoImpl`→high (mismo 0 callers, distinta heurística). POST P0-01 fix: verificar si aún aplica o si interface bridging ya lo resuelve. | `repository_ir.py` → `compute_blast_radius` | 30 min |
73
- | **P1-02** | `project_summary` = primera línea del README (license/marketing). Debe generarse de código: "N-module Spring Boot — M classes, K endpoints, J txn boundaries" | `serializer.py` o `summarizer.py` | 1h |
74
- | **P1-03** | `fix-bug` devuelve 426 archivos para NPE genérico (14% repo, sin `score` field) | `prepare_context.py` → `fix_bug` | 30 min |
75
- | **P1-04** | `indirect_callers:0` para KeycloakSession (1992 direct callers) — BFS para en nivel 1 por hub guard | `repository_ir.py` → `compute_blast_radius` hub guard logic | 45 min |
76
- | **P1-05** | `fix-bug` 23s cold en Keycloak | profiling needed | ~1h |
77
-
78
- ### P2 — Medios (batch)
79
-
80
- - `bounded_contexts: ["dto","file"]` Broadleaf — WRONG. Fix: usar Maven module names
81
- - `role: unknown` para todos `high_coupling_nodes` en modernize — nunca clasifica annotation/interface/entity
82
- - `no_security_signal: 100%` en ambos repos — filter-based security nunca detectado
83
- - JAX-RS sub-resource paths no compuestos con parent `@Path`
84
- - `hotspot_candidates: []` siempre — git churn ignorado
85
- - `--format`/`--no-cache` ausentes en `impact`, `endpoints`, `fix-bug`, `onboard`, `modernize`, `review-pr`
86
- - Architecture confidence diferente entre `--compact` y `--agent` mismo repo
87
-
88
- ---
89
-
90
- ## Primera acción al retomar
91
-
92
- ```bash
93
- cd /Users/user/Documents/workspace/atlas-cli
94
-
95
- # 1. Commitear docs pendientes
96
- git add AUDIT_REAL_REPOS.md docs/PRODUCT_TIERS.md
97
- git commit -m "docs(audit): adversarial audit v1.31.16 — Keycloak + Broadleaf findings"
98
-
99
- # 2. Verificar si P1-01 aún existe post-fix P0-01:
100
- sourcecode impact OrderDaoImpl ~/Documents/workspace/BroadleafCommerce 2>&1 | python3 -m json.tool | grep -E 'risk_level|confidence_level|direct_callers|via_interface'
101
- # Si risk_level sigue siendo inconsistente → fix P1-01
102
- # Si interface bridging ya lo resuelve → skip a P1-02
103
-
104
- # 3. Si P1-01 persiste:
105
- # En compute_blast_radius: cuando direct_callers=0 Y no hay interface bridging Y
106
- # clase es @Service/@Repository impl → bajar confidence, añadir gap en explanation
107
- ```
108
-
109
- ---
110
-
111
- ## Archivos clave del codebase
112
-
113
- ```
114
- src/sourcecode/
115
- repository_ir.py — impact analysis, blast radius, interface bridging (P0-01/02 fixeados aquí)
116
- prepare_context.py — fix-bug, onboard, review-pr output (P1-03 aquí)
117
- serializer.py — compact/agent output, project_summary (P1-02 aquí)
118
- summarizer.py — ProjectSummarizer (P1-02 posiblemente aquí)
119
- cli.py — top-level commands, cache logic
120
- tests/
121
- test_enterprise_benchmarks.py
122
- ```
123
-
124
- ---
125
-
126
- ## Contexto de versiones
127
-
128
- - v1.31.16: bugs auditados en sesión 20
129
- - v1.31.17: versión actual (bumpeada en 0cf28b1 por el usuario)
130
- - Fixes P0-01/P0-02: commit e5fba19 (sesión 21)
131
-
132
- ---
133
-
134
- *Pausado 2026-05-24 — gsd:pause-work (sesión 21)*
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes