sourcecode 1.31.20__tar.gz → 1.31.22__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 (228) hide show
  1. sourcecode-1.31.22/.continue-here.md +88 -0
  2. {sourcecode-1.31.20 → sourcecode-1.31.22}/PKG-INFO +3 -3
  3. {sourcecode-1.31.20 → sourcecode-1.31.22}/README.md +2 -2
  4. {sourcecode-1.31.20 → sourcecode-1.31.22}/pyproject.toml +1 -1
  5. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/__init__.py +1 -1
  6. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/ast_extractor.py +17 -0
  7. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/cli.py +66 -12
  8. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/contract_pipeline.py +21 -1
  9. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/server.py +26 -4
  10. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/prepare_context.py +36 -2
  11. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_audit_fixes.py +10 -3
  12. sourcecode-1.31.22/tests/test_bug_fixes_v1321.py +369 -0
  13. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_bug_fixes_v16.py +82 -0
  14. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_mcp_tools.py +47 -15
  15. sourcecode-1.31.20/.continue-here.md +0 -134
  16. {sourcecode-1.31.20 → sourcecode-1.31.22}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
  17. {sourcecode-1.31.20 → sourcecode-1.31.22}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
  18. {sourcecode-1.31.20 → sourcecode-1.31.22}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
  19. {sourcecode-1.31.20 → sourcecode-1.31.22}/.github/workflows/build-windows.yml +0 -0
  20. {sourcecode-1.31.20 → sourcecode-1.31.22}/.gitignore +0 -0
  21. {sourcecode-1.31.20 → sourcecode-1.31.22}/.ruff.toml +0 -0
  22. {sourcecode-1.31.20 → sourcecode-1.31.22}/.sourcecode-cache/snapshot-3b5997a-fa5c742c.json +0 -0
  23. {sourcecode-1.31.20 → sourcecode-1.31.22}/AUDIT_REAL_REPOS.md +0 -0
  24. {sourcecode-1.31.20 → sourcecode-1.31.22}/CHANGELOG.md +0 -0
  25. {sourcecode-1.31.20 → sourcecode-1.31.22}/CONTRIBUTING.md +0 -0
  26. {sourcecode-1.31.20 → sourcecode-1.31.22}/LICENSE +0 -0
  27. {sourcecode-1.31.20 → sourcecode-1.31.22}/SECURITY.md +0 -0
  28. {sourcecode-1.31.20 → sourcecode-1.31.22}/docs/PRODUCT_TIERS.md +0 -0
  29. {sourcecode-1.31.20 → sourcecode-1.31.22}/docs/privacy.md +0 -0
  30. {sourcecode-1.31.20 → sourcecode-1.31.22}/docs/schema.md +0 -0
  31. {sourcecode-1.31.20 → sourcecode-1.31.22}/raw +0 -0
  32. {sourcecode-1.31.20 → sourcecode-1.31.22}/run_cli.py +0 -0
  33. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/adaptive_scanner.py +0 -0
  34. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/architecture_analyzer.py +0 -0
  35. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/architecture_summary.py +0 -0
  36. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/cache.py +0 -0
  37. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/canonical_ir.py +0 -0
  38. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/classifier.py +0 -0
  39. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/code_notes_analyzer.py +0 -0
  40. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/confidence_analyzer.py +0 -0
  41. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/context_scorer.py +0 -0
  42. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/context_summarizer.py +0 -0
  43. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/contract_model.py +0 -0
  44. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/coverage_parser.py +0 -0
  45. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/dependency_analyzer.py +0 -0
  46. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/__init__.py +0 -0
  47. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/base.py +0 -0
  48. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/csproj_parser.py +0 -0
  49. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/dart.py +0 -0
  50. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/dotnet.py +0 -0
  51. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/elixir.py +0 -0
  52. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/go.py +0 -0
  53. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/heuristic.py +0 -0
  54. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/hybrid.py +0 -0
  55. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/java.py +0 -0
  56. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/jvm_ext.py +0 -0
  57. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/nodejs.py +0 -0
  58. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/parsers.py +0 -0
  59. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/php.py +0 -0
  60. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/project.py +0 -0
  61. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/python.py +0 -0
  62. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/ruby.py +0 -0
  63. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/rust.py +0 -0
  64. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/systems.py +0 -0
  65. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/terraform.py +0 -0
  66. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/detectors/tooling.py +0 -0
  67. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/doc_analyzer.py +0 -0
  68. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/entrypoint_classifier.py +0 -0
  69. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/env_analyzer.py +0 -0
  70. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/file_classifier.py +0 -0
  71. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/flow_analyzer.py +0 -0
  72. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/git_analyzer.py +0 -0
  73. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/graph_analyzer.py +0 -0
  74. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/__init__.py +0 -0
  75. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  76. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  77. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  78. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  79. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  80. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/mcp/runner.py +0 -0
  81. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/metrics_analyzer.py +0 -0
  82. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/output_budget.py +0 -0
  83. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/path_filters.py +0 -0
  84. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/pr_comment_renderer.py +0 -0
  85. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/progress.py +0 -0
  86. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/ranking_engine.py +0 -0
  87. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/redactor.py +0 -0
  88. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/relevance_scorer.py +0 -0
  89. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/repo_classifier.py +0 -0
  90. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/repository_ir.py +0 -0
  91. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/runtime_classifier.py +0 -0
  92. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/scanner.py +0 -0
  93. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/schema.py +0 -0
  94. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/semantic_analyzer.py +0 -0
  95. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/serializer.py +0 -0
  96. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/summarizer.py +0 -0
  97. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/telemetry/__init__.py +0 -0
  98. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/telemetry/config.py +0 -0
  99. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/telemetry/consent.py +0 -0
  100. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/telemetry/events.py +0 -0
  101. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/telemetry/filters.py +0 -0
  102. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/telemetry/transport.py +0 -0
  103. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/tree_utils.py +0 -0
  104. {sourcecode-1.31.20 → sourcecode-1.31.22}/src/sourcecode/workspace.py +0 -0
  105. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/__init__.py +0 -0
  106. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/conftest.py +0 -0
  107. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/coverage.xml +0 -0
  108. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
  109. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/fastapi_app/src/main.py +0 -0
  110. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/go_service/cmd/api/main.go +0 -0
  111. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/go_service/go.mod +0 -0
  112. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/jacoco.xml +0 -0
  113. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/latin1_sample.java +0 -0
  114. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/latin1_sample_iso.java +0 -0
  115. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/lcov.info +0 -0
  116. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
  117. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/nextjs_app/package.json +0 -0
  118. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
  119. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
  120. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
  121. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
  122. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
  123. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
  124. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
  125. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
  126. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
  127. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
  128. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
  129. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
  130. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
  131. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
  132. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
  133. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
  134. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
  135. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
  136. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
  137. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
  138. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
  139. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
  140. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
  141. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
  142. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
  143. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
  144. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
  145. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
  146. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
  147. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
  148. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
  149. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
  150. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
  151. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_architecture_analyzer.py +0 -0
  152. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_architecture_summary.py +0 -0
  153. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_ast_extractor.py +0 -0
  154. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_audit_sas_v2.py +0 -0
  155. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_block1_reliability.py +0 -0
  156. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_block2_coverage.py +0 -0
  157. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_block5_quality.py +0 -0
  158. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_broadleaf_fixes.py +0 -0
  159. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_bug_fixes_v1302.py +0 -0
  160. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_bug_fixes_v13115.py +0 -0
  161. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_bug_fixes_v1312.py +0 -0
  162. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_bug_fixes_v1313.py +0 -0
  163. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_bug_fixes_v2.py +0 -0
  164. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_cache.py +0 -0
  165. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_canonical_ir.py +0 -0
  166. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_classifier.py +0 -0
  167. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_cli.py +0 -0
  168. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_code_notes_analyzer.py +0 -0
  169. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_context_scorer.py +0 -0
  170. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_contract_pipeline.py +0 -0
  171. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_coverage_parser.py +0 -0
  172. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_cross_consistency.py +0 -0
  173. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_dependency_analyzer_node_python.py +0 -0
  174. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_dependency_analyzer_polyglot.py +0 -0
  175. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_dependency_schema.py +0 -0
  176. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_dotnet.py +0 -0
  177. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_go_rust_java.py +0 -0
  178. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_nodejs.py +0 -0
  179. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_php_ruby_dart.py +0 -0
  180. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_python.py +0 -0
  181. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_universal_managed.py +0 -0
  182. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detector_universal_systems.py +0 -0
  183. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_detectors_base.py +0 -0
  184. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_doc_analyzer_jsdom.py +0 -0
  185. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_doc_analyzer_python.py +0 -0
  186. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_encoding_regression.py +0 -0
  187. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_enterprise_benchmarks.py +0 -0
  188. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_graph_analyzer_polyglot.py +0 -0
  189. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_graph_analyzer_python_node.py +0 -0
  190. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_graph_schema.py +0 -0
  191. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_hybrid_inference.py +0 -0
  192. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration.py +0 -0
  193. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_dependencies.py +0 -0
  194. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_detection.py +0 -0
  195. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_docs.py +0 -0
  196. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_graph_modules.py +0 -0
  197. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_lqn.py +0 -0
  198. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_metrics.py +0 -0
  199. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_multistack.py +0 -0
  200. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_semantics.py +0 -0
  201. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_integration_universal.py +0 -0
  202. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_java_spring_integration.py +0 -0
  203. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_mcp_runner.py +0 -0
  204. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_mcp_serve.py +0 -0
  205. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_metrics_analyzer.py +0 -0
  206. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_output_ux.py +0 -0
  207. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_packaging.py +0 -0
  208. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_phase1_improvements.py +0 -0
  209. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_pipeline_integrity.py +0 -0
  210. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_real_projects.py +0 -0
  211. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_redactor.py +0 -0
  212. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_repository_ir.py +0 -0
  213. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_scanner.py +0 -0
  214. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_schema.py +0 -0
  215. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_schema_normalization.py +0 -0
  216. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_scoring_calibration.py +0 -0
  217. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_semantic_analyzer_node.py +0 -0
  218. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_semantic_analyzer_python.py +0 -0
  219. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_semantic_import_resolution.py +0 -0
  220. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_semantic_schema.py +0 -0
  221. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_signal_hierarchy.py +0 -0
  222. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_summarizer.py +0 -0
  223. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_surface_honesty.py +0 -0
  224. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_task_differentiation.py +0 -0
  225. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_telemetry.py +0 -0
  226. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_v131_improvements.py +0 -0
  227. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_v1_10_regressions.py +0 -0
  228. {sourcecode-1.31.20 → sourcecode-1.31.22}/tests/test_workspace_analyzer.py +0 -0
@@ -0,0 +1,88 @@
1
+ # Handoff — sesión 23
2
+ **Date:** 2026-05-25T11:30:00Z
3
+ **Branch:** master
4
+ **Last commit:** `de61d65 fix(mcp): isError + changed-only always-include`
5
+ **Suite:** 59 pass (mcp_tools + mcp_serve + mcp_runner + bug_fixes_v16)
6
+
7
+ ---
8
+
9
+ ## What was done this session
10
+
11
+ Two critical blocking bugs fixed, tested, committed.
12
+
13
+ ### Bug 1 — MCP `isError` always false [FIXED]
14
+
15
+ **Root cause:** FastMCP serializes any plain `dict` return with `isError=false` regardless of content. `_err()` returned a dict → MCP spec §tool-result violated.
16
+
17
+ **Fix:**
18
+ - `_err()` now returns `CallToolResult(isError=True, content=[TextContent(json_payload)])` — passes through FastMCP unmodified
19
+ - `_execute()` also catches `success:false` in CLI JSON output → same `CallToolResult(isError=True)`
20
+ - All validation errors (`INVALID_ARGUMENT`) also get `isError=True`
21
+
22
+ **Files changed:**
23
+ | File | What |
24
+ |------|------|
25
+ | `src/sourcecode/mcp/server.py` | `_err()` returns `CallToolResult`; `_execute()` checks `success:false`; added `json`, `CallToolResult`, `TextContent` imports |
26
+ | `tests/test_mcp_tools.py` | `_assert_failure()` rewritten for `CallToolResult`; envelope tests updated; new test `test_get_impact_context_nonexistent_class_returns_is_error` |
27
+
28
+ ### Bug 2 — `--changed-only` drops security const files [FIXED]
29
+
30
+ **Root cause:** `_resolve_java_constant()` needs const class files in `file_paths` to resolve `ClassName.FIELD_NAME` references in `@M3FiltroSeguridad` annotations. `--changed-only` filtered `sm.file_paths` to only git-changed files → const files absent → `resource_names_unresolved` 2→73.
31
+
32
+ **Fix:** "always-include" function `_is_always_include_ref(p)` added in two places:
33
+ - Criteria: name ends with `Const.java` / `Constants.java` OR path segment in `{security, seguridad, constantes}`
34
+ - Always-include files have `is_changed=False` (read-only anchors, not diff output)
35
+
36
+ **Files changed:**
37
+ | File | What |
38
+ |------|------|
39
+ | `src/sourcecode/contract_pipeline.py` | `changed_only` filter uses `_is_always_include_ref()` |
40
+ | `src/sourcecode/cli.py` | `sm.file_paths` filter in compact mode uses `_is_always_include_ref()` |
41
+ | `tests/test_bug_fixes_v16.py` | New: `test_contract_pipeline_changed_only_always_includes_const_files` + `test_changed_only_resource_names_unresolved_le5` |
42
+
43
+ ---
44
+
45
+ ## Current state
46
+
47
+ Working tree **clean** — 2 untracked files pending decision:
48
+ - `AUDIT_REAL_REPOS.md` — full adversarial audit findings (Keycloak + Broadleaf)
49
+ - `docs/PRODUCT_TIERS.md` — product tiers doc
50
+
51
+ GSD state: milestone v1.0, 13 phases all complete (100%).
52
+
53
+ ---
54
+
55
+ ## P1 bugs remaining (from AUDIT_REAL_REPOS.md)
56
+
57
+ - **BUG-P1-01** Risk score inconsistency: same 0-callers → different risk_level depending on heuristic
58
+ - **BUG-P1-02** `project_summary` copies README blurb instead of generating from code structure
59
+ - **BUG-P1-03** `fix-bug` returns 426 files for generic NPE — no score field, no cap
60
+ - **BUG-P1-04** `indirect_callers: 0` for KeycloakSession (1992 direct callers) — BFS exhausts at depth 1
61
+
62
+ ## P2 bugs remaining
63
+
64
+ - BUG-P2-01 `bounded_contexts` uses utility packages, not Maven modules
65
+ - BUG-P2-02 `role: unknown` for all modernize `high_coupling_nodes`
66
+ - BUG-P2-03 `no_security_signal` always 100% for filter-based security (JAX-RS/XML)
67
+ - BUG-P2-04 JAX-RS sub-resource paths not composed with parent `@Path`
68
+ - BUG-P2-05 Broadleaf admin paths mixed into REST endpoints
69
+ - BUG-P2-06 Architecture confidence inconsistent `--compact` vs `--agent`
70
+ - BUG-P2-07 `entry_points.controllers.methods: 21` vs endpoints 130
71
+ - BUG-P2-08 `--format`/`--no-cache` inconsistently available across subcommands
72
+
73
+ ---
74
+
75
+ ## Next session: where to start
76
+
77
+ **Recommended: BUG-P1-01** — risk score consistency (0 callers → uniform risk_level). Short fix, visible correctness.
78
+
79
+ Then: BUG-P1-04 (BFS depth cap for large fan-in) or BUG-P1-02 (project_summary from code).
80
+
81
+ ---
82
+
83
+ ## Resume
84
+
85
+ ```
86
+ cat .continue-here.md
87
+ python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5
88
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.31.20
3
+ Version: 1.31.22
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.22-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.22
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.22-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.22
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.22"
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.22"
@@ -1131,6 +1131,23 @@ def _detect_role(path: str, contract: FileContract) -> str:
1131
1131
  if any(n.startswith("use") and len(n) > 3 and n[3:4].isupper() for n in export_names):
1132
1132
  return "hook"
1133
1133
 
1134
+ # Angular-specific roles (.ts files): detect by stem suffix before generic checks.
1135
+ # Must run before the "service" path-keyword check to prevent misclassification.
1136
+ if ext == ".ts":
1137
+ _ts_last = Path(path).stem.lower().rsplit(".", 1)[-1]
1138
+ _NG_ROLE_MAP = {
1139
+ "component": "component",
1140
+ "pipe": "pipe",
1141
+ "directive": "directive",
1142
+ "guard": "guard",
1143
+ "interceptor": "interceptor",
1144
+ "resolver": "resolver",
1145
+ "module": "module",
1146
+ "service": "service",
1147
+ }
1148
+ if _ts_last in _NG_ROLE_MAP:
1149
+ return _NG_ROLE_MAP[_ts_last]
1150
+
1134
1151
  # Route / page
1135
1152
  if any(x in path_lower for x in ["/routes/", "/route.", "/pages/", "/api/", "/handlers/"]):
1136
1153
  return "route"
@@ -238,6 +238,21 @@ def _preprocess_argv() -> None:
238
238
  _sys.argv = _sys.argv[:1] + modified
239
239
 
240
240
 
241
+ def _emit_error_json(error: str, message: str, **context: object) -> None:
242
+ """Write a structured JSON error envelope to stderr.
243
+
244
+ Format: {"error": "<code>", "message": "<human text>", ...<context>}
245
+ All CLI validation and runtime errors must go through this helper so that
246
+ agents and tools can parse stderr reliably regardless of error type.
247
+ """
248
+ import json as _json
249
+ import sys as _sys
250
+ payload: dict[str, object] = {"error": error, "message": message}
251
+ payload.update(context)
252
+ _sys.stderr.write(_json.dumps(payload, ensure_ascii=False) + "\n")
253
+ _sys.stderr.flush()
254
+
255
+
241
256
  def _copy_to_clipboard(content: str) -> bool:
242
257
  """Copy text to system clipboard. Returns True on success, False otherwise (never raises)."""
243
258
  import subprocess
@@ -751,21 +766,30 @@ def main(
751
766
 
752
767
  # Validate format choices
753
768
  if format not in FORMAT_CHOICES:
754
- typer.echo(
755
- f"Error: invalid value '{format}' for --format. Valid options: {', '.join(FORMAT_CHOICES)}",
756
- err=True,
769
+ _emit_error_json(
770
+ "invalid_flag_value",
771
+ f"Invalid value '{format}' for --format. Valid values: {', '.join(FORMAT_CHOICES)}.",
772
+ flag="--format",
773
+ value=format,
774
+ valid_values=list(FORMAT_CHOICES),
757
775
  )
758
776
  raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
759
777
  if graph_detail not in GRAPH_DETAIL_CHOICES:
760
- typer.echo(
761
- f"Error: invalid value '{graph_detail}' for --graph-detail. Valid options: {', '.join(GRAPH_DETAIL_CHOICES)}",
762
- err=True,
778
+ _emit_error_json(
779
+ "invalid_flag_value",
780
+ f"Invalid value '{graph_detail}' for --graph-detail. Valid values: {', '.join(GRAPH_DETAIL_CHOICES)}.",
781
+ flag="--graph-detail",
782
+ value=graph_detail,
783
+ valid_values=list(GRAPH_DETAIL_CHOICES),
763
784
  )
764
785
  raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
765
786
  if docs_depth not in DOCS_DEPTH_CHOICES:
766
- typer.echo(
767
- f"Error: invalid value '{docs_depth}' for --docs-depth. Valid options: {', '.join(DOCS_DEPTH_CHOICES)}",
768
- err=True,
787
+ _emit_error_json(
788
+ "invalid_flag_value",
789
+ f"Invalid value '{docs_depth}' for --docs-depth. Valid values: {', '.join(DOCS_DEPTH_CHOICES)}.",
790
+ flag="--docs-depth",
791
+ value=docs_depth,
792
+ valid_values=list(DOCS_DEPTH_CHOICES),
769
793
  )
770
794
  raise typer.Exit(code=2) # FIX-P2-7: arg validation → exit 2
771
795
 
@@ -775,10 +799,18 @@ def main(
775
799
  _raw_path_input = _detected_path[0]
776
800
  target = Path(_raw_path_input).resolve()
777
801
  if not target.exists():
778
- typer.echo(f"Error: directory '{_raw_path_input}' does not exist.", err=True)
802
+ _emit_error_json(
803
+ "directory_not_found",
804
+ f"Directory '{_raw_path_input}' does not exist.",
805
+ path=_raw_path_input,
806
+ )
779
807
  raise typer.Exit(code=1)
780
808
  if not target.is_dir():
781
- typer.echo(f"Error: '{_raw_path_input}' is not a directory.", err=True)
809
+ _emit_error_json(
810
+ "not_a_directory",
811
+ f"Path '{_raw_path_input}' is not a directory.",
812
+ path=_raw_path_input,
813
+ )
782
814
  raise typer.Exit(code=1)
783
815
 
784
816
  # Normalize mode aliases
@@ -1201,6 +1233,15 @@ def main(
1201
1233
  workspace_root = target / workspace.path
1202
1234
  if not workspace_root.exists() or not workspace_root.is_dir():
1203
1235
  continue
1236
+ # BUG-2: skip workspaces explicitly excluded via --exclude.
1237
+ # Without this guard, excluded frontend/backend modules still contribute
1238
+ # their stacks and entry_points, causing architecture_summary to describe
1239
+ # stacks that were intentionally filtered out.
1240
+ if _extra_excludes:
1241
+ _ws_norm = workspace.path.replace("\\", "/").strip("/")
1242
+ _ws_parts = frozenset(_ws_norm.split("/"))
1243
+ if _ws_parts & _extra_excludes:
1244
+ continue
1204
1245
  _ws_topology = RepoClassifier().classify(workspace_root)
1205
1246
  workspace_scanner = AdaptiveScanner(workspace_root, topology=_ws_topology, base_depth=depth)
1206
1247
  workspace_tree = filter_sensitive_files(workspace_scanner.scan_tree())
@@ -1776,8 +1817,21 @@ def main(
1776
1817
  if changed_only and _allowed_changed_files:
1777
1818
  # GAP-5: preserve full entry_points for architecture context even in
1778
1819
  # --changed-only mode. Only filter file_paths and code_notes.
1820
+ # ALWAYS-INCLUDE: security-const files must stay in file_paths even when
1821
+ # not in the git diff — they resolve Java constant references used in
1822
+ # @M3FiltroSeguridad annotations (read-only anchors, not diff output).
1823
+ def _is_always_include_ref(p: str) -> bool:
1824
+ name = p.rsplit("/", 1)[-1].rsplit("\\", 1)[-1]
1825
+ if name.endswith("Const.java") or name.endswith("Constants.java"):
1826
+ return True
1827
+ parts = p.replace("\\", "/").lower().split("/")
1828
+ return any(seg in ("security", "seguridad", "constantes") for seg in parts)
1829
+
1779
1830
  sm = _replace(sm,
1780
- file_paths=[p for p in sm.file_paths if p in _allowed_changed_files],
1831
+ file_paths=[
1832
+ p for p in sm.file_paths
1833
+ if p in _allowed_changed_files or _is_always_include_ref(p)
1834
+ ],
1781
1835
  code_notes=[n for n in sm.code_notes if n.path in _allowed_changed_files],
1782
1836
  )
1783
1837
  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()
@@ -589,6 +589,15 @@ _ARTIFACT_CHANGE_EFFECT: dict[str, str] = {
589
589
  "documentation": "documentation file (no runtime impact)",
590
590
  "ide_noise": "IDE/tooling artifact (no application impact)",
591
591
  "source": "application source file (role could not be confirmed from code signals)",
592
+ # Angular-specific artifact types (detected before Java/Spring heuristics)
593
+ "ng_component": "UI presentation layer (Angular @Component)",
594
+ "ng_pipe": "data transformation layer (Angular @Pipe)",
595
+ "ng_directive": "DOM behavior layer (Angular @Directive)",
596
+ "ng_guard": "navigation guard (Angular CanActivate / CanActivateFn)",
597
+ "ng_interceptor": "HTTP middleware layer (Angular HttpInterceptor)",
598
+ "ng_resolver": "data pre-fetch layer (Angular Resolve)",
599
+ "ng_service": "Angular injectable service (@Injectable)",
600
+ "ng_module": "Angular feature module (@NgModule)",
592
601
  }
593
602
 
594
603
  # Maps frontend symptom keywords → backend terms likely to contain the root cause.
@@ -2710,6 +2719,29 @@ class TaskContextBuilder:
2710
2719
  ):
2711
2720
  return {"artifact_type": "entrypoint", "risk_areas": ["api", "config"], "impact_level": "critical", "is_noise": False, "module": module, "confidence": "high"}
2712
2721
 
2722
+ # Angular-specific artifact detection (.ts files only).
2723
+ # Must run BEFORE the Java/Spring heuristics so that *.component.ts,
2724
+ # *.pipe.ts, etc. are never misclassified as "service" or "security".
2725
+ # Detection is pure-path/stem — no file reads, fully deterministic.
2726
+ if suffix == ".ts":
2727
+ # Stem may be multi-part: "causa-denegacion-form.component" → last part is the Angular type
2728
+ _ts_last = stem_lower.rsplit(".", 1)[-1] # "component", "pipe", etc.
2729
+ _NG_SUFFIX_MAP = {
2730
+ "component": ("ng_component", ["ui"], "medium"),
2731
+ "pipe": ("ng_pipe", ["ui"], "low"),
2732
+ "directive": ("ng_directive", ["ui"], "medium"),
2733
+ "guard": ("ng_guard", ["security", "auth"], "high"),
2734
+ "interceptor": ("ng_interceptor", ["api"], "medium"),
2735
+ "resolver": ("ng_resolver", ["api"], "low"),
2736
+ "module": ("ng_module", ["config"], "medium"),
2737
+ }
2738
+ if _ts_last in _NG_SUFFIX_MAP:
2739
+ _ng_atype, _ng_risks, _ng_impact = _NG_SUFFIX_MAP[_ts_last]
2740
+ return {"artifact_type": _ng_atype, "risk_areas": _ng_risks, "impact_level": _ng_impact, "is_noise": False, "module": module, "confidence": "high"}
2741
+ # Angular service: stem ends with ".service" or equals "service"
2742
+ if _ts_last == "service":
2743
+ return {"artifact_type": "ng_service", "risk_areas": ["business_logic"], "impact_level": "medium", "is_noise": False, "module": module, "confidence": "high"}
2744
+
2713
2745
  # Security surface (extended: interceptor, filter, cors, acl)
2714
2746
  _SECURITY_KW = ("security", "auth", "jwt", "token", "permission", "role",
2715
2747
  "credential", "encrypt", "decrypt", "oauth", "saml", "ldap",
@@ -2724,9 +2756,11 @@ class TaskContextBuilder:
2724
2756
  if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _API_KW):
2725
2757
  return {"artifact_type": "controller", "risk_areas": ["api"], "impact_level": "high", "is_noise": False, "module": module, "confidence": "high"}
2726
2758
 
2727
- # Business logic / services (extended: facade, usecase, aspect, listener, component)
2759
+ # Business logic / services (extended: facade, usecase, aspect, listener)
2760
+ # NOTE: "component" intentionally removed — Angular *.component.ts files
2761
+ # are caught above by the Angular-specific block before reaching here.
2728
2762
  _SERVICE_KW = ("service", "serviceimpl", "servicefacade", "facade", "usecase",
2729
- "interactor", "aspect", "listener", "subscriber", "eventhandler", "component")
2763
+ "interactor", "aspect", "listener", "subscriber", "eventhandler")
2730
2764
  if suffix in _CODE_EXTS and any(kw in stem_lower for kw in _SERVICE_KW):
2731
2765
  return {"artifact_type": "service", "risk_areas": ["transactions", "business_logic"], "impact_level": "high", "is_noise": False, "module": module, "confidence": "high"}
2732
2766
 
@@ -814,12 +814,19 @@ class TestTelemetrySchema:
814
814
  )
815
815
 
816
816
  def test_telemetry_invalid_action_returns_error_with_valid_list(self):
817
+ import json
818
+ from mcp.types import CallToolResult
817
819
  from sourcecode.mcp.server import telemetry
818
820
  result = telemetry("unknown_action")
819
- assert result["success"] is False
820
- assert result["error"] is not None
821
+ assert isinstance(result, CallToolResult), (
822
+ f"expected CallToolResult, got {type(result).__name__}"
823
+ )
824
+ assert result.isError is True, "isError must be True for tool failures"
825
+ payload = json.loads(result.content[0].text)
826
+ assert payload["success"] is False
827
+ assert payload["error"] is not None
821
828
  # Error message must enumerate valid actions
822
- msg = result["error"]["message"]
829
+ msg = payload["error"]["message"]
823
830
  for action in ("status", "enable", "disable"):
824
831
  assert action in msg, (
825
832
  f"telemetry error message must list valid action '{action}': {msg!r}"