sourcecode 1.5.0__tar.gz → 1.7.0__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.
- sourcecode-1.7.0/.continue-here.md +105 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/PKG-INFO +1 -1
- {sourcecode-1.5.0 → sourcecode-1.7.0}/pyproject.toml +1 -1
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/adaptive_scanner.py +2 -1
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/cli.py +61 -44
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/contract_pipeline.py +19 -3
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/java.py +40 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/prepare_context.py +74 -5
- sourcecode-1.7.0/src/sourcecode/progress.py +110 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/serializer.py +4 -0
- sourcecode-1.7.0/tests/test_bug_fixes_v16.py +250 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/.gitignore +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/.ruff.toml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/LICENSE +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/README.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/SECURITY.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/docs/privacy.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/docs/schema.md +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/raw +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/run_cli.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/__init__.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/conftest.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_cli.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_schema.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.5.0 → sourcecode-1.7.0}/tests/test_workspace_analyzer.py +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Continue Here — sourcecode Java/Spring Enterprise Bug Fixes v1.5.x
|
|
2
|
+
**Created:** 2026-05-08
|
|
3
|
+
**Session context:** Ad-hoc benchmark-driven bug fixes (not a GSD phase execution)
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What Was Done This Session
|
|
8
|
+
|
|
9
|
+
Applied 5 fixes targeting regressions in v1.5.0 benchmark report on saint-server (3681 .java, 460 *RestController*, 52 *Mapper.xml, Spring Boot 2.7.18 DDD+legacy layout).
|
|
10
|
+
|
|
11
|
+
### Root Causes Found and Fixed
|
|
12
|
+
|
|
13
|
+
| ID | File(s) | Root Cause | Fix |
|
|
14
|
+
|----|---------|-----------|-----|
|
|
15
|
+
| N1 | `detectors/java.py` | Scanner `depth >= max_depth=10` cuts `ddd/*/infraestructure/rest/` files silently. Path `src/main/java/com/m3/saint/ddd/domain/infraestructure/rest/` = 10 parts → excluded from file_tree → not in entry_points. | New `_augment_deep_java_controllers()`: direct `os.walk` of `src/main/java/` for `*Controller*.java` files not in file_tree |
|
|
16
|
+
| G3 | `contract_pipeline.py` | Mapper.xml score=0.3 (no `.xml` suffix boost) < Java score=0.4. 3000+ files compete for 2500-file cap — all 52 Mapper.xml cut. | `_is_priority()`: Mapper.xml gets same priority as entry_points in cap sort |
|
|
17
|
+
| T11 | `contract_pipeline.py` | Centrality sort key ignored `is_entrypoint`. REST controllers have low fan_in → ranked below JPA entities. | Added `-c.is_entrypoint` as primary key in centrality sort |
|
|
18
|
+
| P3-B | `cli.py` | Auto-switch to centrality when DDD architecture detected — actively hurt REST controller visibility. | Removed 5-line P3-B block |
|
|
19
|
+
| N2 | `serializer.py` | `hotspots` removed from top-level output in v1.5.0, only in `semantic_summary`. Breaking for consumers using `d["hotspots"]`. | Restore `result["hotspots"]` at top level when `semantic_summary.hotspots` exists |
|
|
20
|
+
|
|
21
|
+
### Modified Files (uncommitted — user requested no commits)
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/sourcecode/cli.py # P3-B removed (DDD auto-centrality switch)
|
|
25
|
+
src/sourcecode/contract_pipeline.py # G3: _is_priority() + T11: centrality sort key
|
|
26
|
+
src/sourcecode/detectors/java.py # N1: _augment_deep_java_controllers() method
|
|
27
|
+
src/sourcecode/serializer.py # N2: result["hotspots"] top-level restored
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Test Status
|
|
31
|
+
- 651 passed, 3 skipped (subset excluding pre-existing failures) — zero regressions
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## What Remains
|
|
36
|
+
|
|
37
|
+
### Not Fixed (deferred)
|
|
38
|
+
|
|
39
|
+
| ID | Description |
|
|
40
|
+
|----|-------------|
|
|
41
|
+
| M1 | `prepare-context` from monorepo root detects Angular instead of saint-server Java. Requires monorepo subproject routing in `prepare_context.py` → `TaskContextBuilder` / `RepoClassifier`. |
|
|
42
|
+
| M2 | `@M3FiltroSeguridad` custom security annotation not aggregated into security map |
|
|
43
|
+
| M3 | Spring profiles detected but not linked to `application-{profile}.yml` files |
|
|
44
|
+
|
|
45
|
+
### Verification Commands (run against saint-server after commit)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# N1: DDD controllers now captured (need --depth 12+ for tree, augment works at any depth)
|
|
49
|
+
sourcecode saint-server --depth 12 --entrypoints-only --format json | python -c "
|
|
50
|
+
import sys, json
|
|
51
|
+
d = json.load(sys.stdin)
|
|
52
|
+
rest = [x for x in d.get('contracts', []) if 'RestController' in x.get('path','')]
|
|
53
|
+
ddd = [x for x in rest if '/ddd/' in x['path'] or '/infraestructure/' in x['path']]
|
|
54
|
+
print(f'REST total: {len(rest)} | DDD: {len(ddd)}')"
|
|
55
|
+
|
|
56
|
+
# G3: MyBatis XML in contracts
|
|
57
|
+
sourcecode saint-server --depth 10 --format json | python -c "
|
|
58
|
+
import sys, json
|
|
59
|
+
d = json.load(sys.stdin)
|
|
60
|
+
xml = [x for x in d.get('contracts', []) if x.get('language') == 'mybatis-xml']
|
|
61
|
+
print(f'MyBatis contracts: {len(xml)}')"
|
|
62
|
+
|
|
63
|
+
# N2: hotspots at top level restored
|
|
64
|
+
sourcecode saint-server --semantics --format json | python -c "
|
|
65
|
+
import sys, json
|
|
66
|
+
d = json.load(sys.stdin)
|
|
67
|
+
print('top-level hotspots:', len(d.get('hotspots', [])))
|
|
68
|
+
print('semantic_summary.hotspots:', len(d.get('semantic_summary',{}).get('hotspots',[])))"
|
|
69
|
+
|
|
70
|
+
# T11: centrality now surfaces REST controllers
|
|
71
|
+
sourcecode saint-server --rank-by centrality --depth 10 --format json | python -c "
|
|
72
|
+
import sys, json
|
|
73
|
+
d = json.load(sys.stdin)
|
|
74
|
+
rest = [x for x in d.get('contracts', []) if 'RestController' in x.get('path','')]
|
|
75
|
+
print(f'REST in centrality: {len(rest)}')"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Note on N1 depth:** `_augment_deep_java_controllers` bypasses the scanner depth limit and always walks `src/main/java/` directly. DDD controllers will be detected as entry_points regardless of `--depth`. However, for these same files to appear in the general file_paths (not just entry_points), the scanner itself needs `--depth >= 11`.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Previous Session Fixes (already committed)
|
|
83
|
+
|
|
84
|
+
C1/C2/C3/G1/G2/G3 from v1.4.0→v1.5.0:
|
|
85
|
+
- `--mode standard` key `file_contracts` → `contracts`
|
|
86
|
+
- Controller-first scan, cap 500→1000
|
|
87
|
+
- DDD bounded contexts from package structure
|
|
88
|
+
- Maven BOM limitation warning
|
|
89
|
+
- MyBatis XML extractor (code existed but files were cut by cap — fixed in this session)
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## GSD Project State (unchanged)
|
|
94
|
+
|
|
95
|
+
All 13 phases complete (see `.planning/STATE.md`). Both sessions were ad-hoc bug work from benchmark reports, not planned phase executions.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Resume Instructions
|
|
100
|
+
|
|
101
|
+
1. `git diff --stat` — verify 4 uncommitted files
|
|
102
|
+
2. `python -m pytest tests/ --ignore=tests/test_block2_coverage.py --ignore=tests/test_dependency_analyzer_node_python.py --ignore=tests/test_pipeline_integrity.py -q`
|
|
103
|
+
3. Commit the fixes
|
|
104
|
+
4. Run verification commands above against saint-server
|
|
105
|
+
5. If tackling M1, entry point is `prepare_context.py:TaskContextBuilder.build()` + `repo_classifier.py`
|
|
@@ -135,7 +135,8 @@ class AdaptiveScanner:
|
|
|
135
135
|
if current_depth >= n:
|
|
136
136
|
# At or inside the source root
|
|
137
137
|
if rel_parts[:n] == src_parts:
|
|
138
|
-
|
|
138
|
+
# base_depth acts as a floor — explicit --depth always wins
|
|
139
|
+
return max(src_max, self.base_depth)
|
|
139
140
|
else:
|
|
140
141
|
# Ancestor check: src_parts starts with rel_parts?
|
|
141
142
|
if src_parts[:current_depth] == rel_parts:
|
|
@@ -10,6 +10,7 @@ import typer
|
|
|
10
10
|
|
|
11
11
|
from sourcecode import __version__
|
|
12
12
|
from sourcecode.entrypoint_classifier import is_production_entry_point, normalize_entry_point
|
|
13
|
+
from sourcecode.progress import Progress
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
# ---------------------------------------------------------------------------
|
|
@@ -138,18 +139,19 @@ def _check_pipeline_coherence(sm: "SourceMap") -> list[str]: # type: ignore[nam
|
|
|
138
139
|
return issues
|
|
139
140
|
|
|
140
141
|
_HELP = """\
|
|
141
|
-
|
|
142
|
+
Compressed AI-ready context for Java/Spring enterprise codebases.
|
|
142
143
|
|
|
143
|
-
[bold]
|
|
144
|
-
sourcecode
|
|
145
|
-
sourcecode
|
|
146
|
-
sourcecode --
|
|
144
|
+
[bold]Examples:[/bold]
|
|
145
|
+
sourcecode saint-server --compact
|
|
146
|
+
sourcecode . --changed-only --git-context
|
|
147
|
+
sourcecode saint-server --symbol SeguridadRestController
|
|
148
|
+
sourcecode prepare-context onboard saint-server
|
|
149
|
+
sourcecode prepare-context delta . --since main
|
|
147
150
|
|
|
148
151
|
[bold]Subcommands:[/bold]
|
|
149
|
-
prepare-context TASK [PATH] [dim]# task-specific context[/dim]
|
|
152
|
+
prepare-context TASK [PATH] [dim]# task-specific context (onboard, delta, fix-bug, ...)[/dim]
|
|
150
153
|
telemetry status|enable|disable
|
|
151
154
|
version
|
|
152
|
-
config
|
|
153
155
|
"""
|
|
154
156
|
|
|
155
157
|
# Known subcommand names — tokens matching these are routed as subcommands,
|
|
@@ -382,28 +384,28 @@ def main(
|
|
|
382
384
|
graph_modules: bool = typer.Option(
|
|
383
385
|
False,
|
|
384
386
|
"--graph-modules",
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
"Useful for understanding coupling and call flows. Adds module_graph to output. "
|
|
388
|
-
"Combine with --graph-detail and --graph-edges to control scope."
|
|
389
|
-
),
|
|
387
|
+
hidden=True,
|
|
388
|
+
help="Include a structural module graph in output.",
|
|
390
389
|
),
|
|
391
390
|
graph_detail: str = typer.Option(
|
|
392
391
|
"high",
|
|
393
392
|
"--graph-detail",
|
|
394
|
-
|
|
393
|
+
hidden=True,
|
|
394
|
+
help="Detail level for --graph-modules: high, medium, full.",
|
|
395
395
|
show_default=True,
|
|
396
396
|
),
|
|
397
397
|
max_nodes: Optional[int] = typer.Option(
|
|
398
398
|
None,
|
|
399
399
|
"--max-nodes",
|
|
400
|
-
|
|
400
|
+
hidden=True,
|
|
401
|
+
help="Maximum nodes in --graph-modules output.",
|
|
401
402
|
min=1,
|
|
402
403
|
),
|
|
403
404
|
graph_edges: Optional[str] = typer.Option(
|
|
404
405
|
None,
|
|
405
406
|
"--graph-edges",
|
|
406
|
-
|
|
407
|
+
hidden=True,
|
|
408
|
+
help="Edge types for --graph-modules, comma-separated: imports,calls,contains,extends.",
|
|
407
409
|
),
|
|
408
410
|
no_tree: bool = typer.Option(
|
|
409
411
|
False,
|
|
@@ -414,10 +416,8 @@ def main(
|
|
|
414
416
|
tree: bool = typer.Option(
|
|
415
417
|
False,
|
|
416
418
|
"--tree",
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
"Adds significant size — use when the agent needs to browse the full file structure."
|
|
420
|
-
),
|
|
419
|
+
hidden=True,
|
|
420
|
+
help="Include the full file_tree and flat file_paths list in output.",
|
|
421
421
|
),
|
|
422
422
|
no_redact: bool = typer.Option(
|
|
423
423
|
False,
|
|
@@ -448,41 +448,33 @@ def main(
|
|
|
448
448
|
docs: bool = typer.Option(
|
|
449
449
|
False,
|
|
450
450
|
"--docs",
|
|
451
|
-
|
|
451
|
+
hidden=True,
|
|
452
|
+
help="Extract documentation: docstrings, function signatures, and module-level comments.",
|
|
452
453
|
),
|
|
453
454
|
docs_depth: str = typer.Option(
|
|
454
455
|
"symbols",
|
|
455
456
|
"--docs-depth",
|
|
456
|
-
|
|
457
|
+
hidden=True,
|
|
458
|
+
help="Documentation extraction depth: module, symbols, full.",
|
|
457
459
|
show_default=True,
|
|
458
460
|
),
|
|
459
461
|
full_metrics: bool = typer.Option(
|
|
460
462
|
False,
|
|
461
463
|
"--full-metrics",
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
"Produces file_metrics and metrics_summary. "
|
|
465
|
-
"Not included in --agent output — designed for CI pipelines and code review tools, not as primary agent context."
|
|
466
|
-
),
|
|
464
|
+
hidden=True,
|
|
465
|
+
help="Technical audit: LOC, complexity, test coverage per file.",
|
|
467
466
|
),
|
|
468
467
|
semantics: bool = typer.Option(
|
|
469
468
|
False,
|
|
470
469
|
"--semantics",
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
"Adds semantic_calls, semantic_symbols, semantic_links, semantic_summary, and hotspots (files ranked by fan-in/fan-out). "
|
|
474
|
-
"Slower than default analysis — skip for quick scans. "
|
|
475
|
-
"Confidence degrades on dynamic dispatch, decorators, and generated code."
|
|
476
|
-
),
|
|
470
|
+
hidden=True,
|
|
471
|
+
help="Cross-file symbol resolution and call graph analysis.",
|
|
477
472
|
),
|
|
478
473
|
architecture: bool = typer.Option(
|
|
479
474
|
False,
|
|
480
475
|
"--architecture",
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
"and dominant structural patterns. Adds architecture to output. "
|
|
484
|
-
"Confidence is low when based on directory names alone — combine with --semantics for higher accuracy."
|
|
485
|
-
),
|
|
476
|
+
hidden=True,
|
|
477
|
+
help="Architectural layer inference (MVC/hexagonal/layered).",
|
|
486
478
|
),
|
|
487
479
|
git_context: bool = typer.Option(
|
|
488
480
|
False,
|
|
@@ -788,7 +780,7 @@ def main(
|
|
|
788
780
|
# Require at least 8: src(1)+main(2)+java(3)+com(4)+co(5)+app(6)+module(7)+file.
|
|
789
781
|
_java_manifest_names = {"pom.xml", "build.gradle", "build.gradle.kts"}
|
|
790
782
|
_is_java = any(Path(m).name in _java_manifest_names for m in manifests)
|
|
791
|
-
_java_min_depth =
|
|
783
|
+
_java_min_depth = 12
|
|
792
784
|
effective_depth = max(depth, _java_min_depth) if _is_java and depth < _java_min_depth else depth
|
|
793
785
|
|
|
794
786
|
# --agent: enable signal analyzers; output via agent_view (not compact)
|
|
@@ -1399,11 +1391,30 @@ def main(
|
|
|
1399
1391
|
))
|
|
1400
1392
|
sm = _replace(sm, pipeline_trace=_trace.build_trace())
|
|
1401
1393
|
|
|
1402
|
-
#
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1394
|
+
# Pre-compute uncommitted files for --changed-only.
|
|
1395
|
+
# The contract pipeline filter and git_context are two separate subsystems;
|
|
1396
|
+
# wire them here so the pipeline uses git_context data, not an independent git call.
|
|
1397
|
+
_allowed_changed_files: Optional[set[str]] = None
|
|
1398
|
+
if changed_only:
|
|
1399
|
+
from sourcecode.git_analyzer import GitAnalyzer as _GitAnalyzerEarly
|
|
1400
|
+
try:
|
|
1401
|
+
_gc_early = _GitAnalyzerEarly().analyze(target, depth=1, days=1)
|
|
1402
|
+
_bad_gc = {"no_git_repo", "git_not_found", "git_timeout"}
|
|
1403
|
+
if _gc_early and not (_bad_gc & set(_gc_early.limitations)):
|
|
1404
|
+
_uc = _gc_early.uncommitted_changes
|
|
1405
|
+
if _uc:
|
|
1406
|
+
_allowed_changed_files = (
|
|
1407
|
+
set(_uc.staged) | set(_uc.unstaged) | set(_uc.untracked)
|
|
1408
|
+
)
|
|
1409
|
+
if not _allowed_changed_files:
|
|
1410
|
+
typer.echo(
|
|
1411
|
+
"[changed-only] git unavailable or no uncommitted changes — falling back to full scan.",
|
|
1412
|
+
err=True,
|
|
1413
|
+
)
|
|
1414
|
+
changed_only = False
|
|
1415
|
+
except Exception:
|
|
1416
|
+
typer.echo("[changed-only] git error — falling back to full scan.", err=True)
|
|
1417
|
+
changed_only = False
|
|
1407
1418
|
|
|
1408
1419
|
# Contract pipeline — runs for mode=contract|standard|deep|hybrid (skip for raw)
|
|
1409
1420
|
_is_contract_mode = mode in ("contract", "standard")
|
|
@@ -1438,6 +1449,7 @@ def main(
|
|
|
1438
1449
|
max_importers=max_importers,
|
|
1439
1450
|
semantic_calls=sm.semantic_calls or None,
|
|
1440
1451
|
code_notes=sm.code_notes or None,
|
|
1452
|
+
allowed_changed_files=_allowed_changed_files,
|
|
1441
1453
|
**_java_pipeline_kwargs,
|
|
1442
1454
|
)
|
|
1443
1455
|
except Exception as _exc:
|
|
@@ -1712,8 +1724,13 @@ def prepare_context_cmd(
|
|
|
1712
1724
|
if llm_prompt:
|
|
1713
1725
|
out["llm_prompt"] = builder.render_prompt(output)
|
|
1714
1726
|
|
|
1727
|
+
import sys as _sys
|
|
1715
1728
|
_pc_content = json.dumps(out, indent=2, ensure_ascii=False)
|
|
1716
|
-
|
|
1729
|
+
_pc_bytes = _pc_content.encode("utf-8")
|
|
1730
|
+
_sys.stdout.buffer.write(_pc_bytes)
|
|
1731
|
+
if not _pc_content.endswith("\n"):
|
|
1732
|
+
_sys.stdout.buffer.write(b"\n")
|
|
1733
|
+
_sys.stdout.buffer.flush()
|
|
1717
1734
|
|
|
1718
1735
|
if copy:
|
|
1719
1736
|
_trimmed = _pc_content.strip()
|
|
@@ -183,6 +183,7 @@ class ContractPipeline:
|
|
|
183
183
|
code_notes: Optional[list] = None,
|
|
184
184
|
max_contracts: Optional[int] = _MAX_CONTRACTS,
|
|
185
185
|
min_score: Optional[float] = None,
|
|
186
|
+
allowed_changed_files: Optional[set[str]] = None,
|
|
186
187
|
) -> tuple[list[FileContract], ContractSummary]:
|
|
187
188
|
"""Run the full extraction pipeline.
|
|
188
189
|
|
|
@@ -200,9 +201,14 @@ class ContractPipeline:
|
|
|
200
201
|
engine = RankingEngine(monorepo_packages)
|
|
201
202
|
|
|
202
203
|
# 1. Changed files (for --changed-only and ranking)
|
|
204
|
+
# When allowed_changed_files is provided (from pre-computed git uncommitted_changes),
|
|
205
|
+
# use it directly. Otherwise fall back to independent git commands.
|
|
203
206
|
changed_files: set[str] = set()
|
|
204
207
|
if changed_only or rank_by == "git-churn":
|
|
205
|
-
|
|
208
|
+
if allowed_changed_files is not None:
|
|
209
|
+
changed_files = allowed_changed_files
|
|
210
|
+
else:
|
|
211
|
+
changed_files = _get_changed_files(root)
|
|
206
212
|
|
|
207
213
|
# 2. Select files to extract
|
|
208
214
|
# Exclude test files by default — they dominate by count but add noise
|
|
@@ -240,10 +246,18 @@ class ContractPipeline:
|
|
|
240
246
|
|
|
241
247
|
# Apply max_files cap — bypass when symbol search to ensure defining files are found.
|
|
242
248
|
# A symbol query over a large repo needs all files; result set is small after filtering.
|
|
249
|
+
# MyBatis Mapper.xml contracts rank below Java files on path score alone (.xml has no
|
|
250
|
+
# suffix boost). Give them the same priority slot as entry_points so they survive the cap.
|
|
251
|
+
def _is_priority(p: str) -> bool:
|
|
252
|
+
if p in entry_paths:
|
|
253
|
+
return True
|
|
254
|
+
name = p.rsplit("/", 1)[-1]
|
|
255
|
+
return name.lower().endswith("mapper.xml")
|
|
256
|
+
|
|
243
257
|
if symbol is None and len(src_paths) > self.max_files:
|
|
244
258
|
src_paths = sorted(
|
|
245
259
|
src_paths,
|
|
246
|
-
key=lambda p: (p
|
|
260
|
+
key=lambda p: (_is_priority(p), scorer.score(p)),
|
|
247
261
|
reverse=True,
|
|
248
262
|
)[:self.max_files]
|
|
249
263
|
|
|
@@ -362,7 +376,9 @@ class ContractPipeline:
|
|
|
362
376
|
|
|
363
377
|
def _rank(self, contracts: list[FileContract], rank_by: RankStrategy) -> list[FileContract]:
|
|
364
378
|
if rank_by == "centrality":
|
|
365
|
-
|
|
379
|
+
# Entrypoints (REST controllers, main classes) surface first even in centrality mode:
|
|
380
|
+
# they have low fan_in (not imported) but are the primary API surface.
|
|
381
|
+
return sorted(contracts, key=lambda c: (-c.is_entrypoint, -(c.fan_in + c.fan_out), c.path))
|
|
366
382
|
if rank_by == "git-churn":
|
|
367
383
|
return sorted(contracts, key=lambda c: (-c.is_changed, -c.relevance_score, c.path))
|
|
368
384
|
# Default: relevance — path breaks ties deterministically
|
|
@@ -225,6 +225,12 @@ class JavaDetector(AbstractDetector):
|
|
|
225
225
|
all_paths = flatten_file_tree(context.file_tree)
|
|
226
226
|
all_java = [p for p in all_paths if p.endswith(".java")]
|
|
227
227
|
|
|
228
|
+
# Augment with a direct scan of standard Java source roots for Controller-named
|
|
229
|
+
# files that the depth-limited file_tree scanner may have missed.
|
|
230
|
+
# DDD layouts place REST controllers at depth 10+ (e.g.
|
|
231
|
+
# src/main/java/com/org/app/ddd/domain/infraestructure/rest/XxxRestController.java).
|
|
232
|
+
self._augment_deep_java_controllers(context, all_java)
|
|
233
|
+
|
|
228
234
|
# 1. @SpringBootApplication entry: Application.java / Main.java by name
|
|
229
235
|
app_candidates = [
|
|
230
236
|
p for p in all_java
|
|
@@ -270,6 +276,40 @@ class JavaDetector(AbstractDetector):
|
|
|
270
276
|
unique_eps.append(ep)
|
|
271
277
|
return unique_eps
|
|
272
278
|
|
|
279
|
+
def _augment_deep_java_controllers(self, context: DetectionContext, all_java: list[str]) -> None:
|
|
280
|
+
"""Scan standard Java source roots for *Controller*.java files not in all_java.
|
|
281
|
+
|
|
282
|
+
The depth-limited file_tree scanner misses files at depth >= max_depth.
|
|
283
|
+
DDD layouts place REST controllers deep (e.g. depth 10+), so we supplement
|
|
284
|
+
with a direct filesystem walk scoped to the standard Maven/Gradle source root.
|
|
285
|
+
"""
|
|
286
|
+
import os as _os
|
|
287
|
+
existing = set(all_java)
|
|
288
|
+
# Standard Java source root candidates (Maven first, then Gradle/other)
|
|
289
|
+
_SRC_ROOTS = ("src/main/java", "src/main/kotlin", "src/java", "src")
|
|
290
|
+
for src_root_name in _SRC_ROOTS:
|
|
291
|
+
src_root = context.root / src_root_name
|
|
292
|
+
if not src_root.is_dir():
|
|
293
|
+
continue
|
|
294
|
+
try:
|
|
295
|
+
for dirpath, _dirs, filenames in _os.walk(str(src_root)):
|
|
296
|
+
for fname in filenames:
|
|
297
|
+
if "Controller" not in fname or not fname.endswith(".java"):
|
|
298
|
+
continue
|
|
299
|
+
full = Path(dirpath) / fname
|
|
300
|
+
if full.is_symlink():
|
|
301
|
+
continue
|
|
302
|
+
try:
|
|
303
|
+
rel = str(full.relative_to(context.root)).replace("\\", "/")
|
|
304
|
+
if rel not in existing:
|
|
305
|
+
all_java.append(rel)
|
|
306
|
+
existing.add(rel)
|
|
307
|
+
except ValueError:
|
|
308
|
+
pass
|
|
309
|
+
except OSError:
|
|
310
|
+
pass
|
|
311
|
+
return # use only first matching source root
|
|
312
|
+
|
|
273
313
|
def _scan_java_file_for_entry_points(self, abs_path: Path, rel_path: str) -> list[EntryPoint]:
|
|
274
314
|
try:
|
|
275
315
|
if abs_path.stat().st_size > _MAX_FILE_SIZE:
|
|
@@ -398,11 +398,27 @@ class TaskContextBuilder:
|
|
|
398
398
|
from sourcecode.tree_utils import flatten_file_tree
|
|
399
399
|
|
|
400
400
|
_topology = RepoClassifier().classify(self.root)
|
|
401
|
-
|
|
401
|
+
# Shallow pre-scan to detect Java manifests before choosing depth.
|
|
402
|
+
from sourcecode.scanner import FileScanner as _FileScanner
|
|
403
|
+
_pre = _FileScanner(self.root, max_depth=1)
|
|
404
|
+
_pre_manifests = _pre.find_manifests()
|
|
405
|
+
_java_names = {"pom.xml", "build.gradle", "build.gradle.kts"}
|
|
406
|
+
_is_java = any(Path(m).name in _java_names for m in _pre_manifests)
|
|
407
|
+
_base_depth = 12 if _is_java else 6
|
|
408
|
+
scanner = AdaptiveScanner(self.root, topology=_topology, base_depth=_base_depth)
|
|
402
409
|
file_tree = scanner.scan_tree()
|
|
403
410
|
manifests = scanner.find_manifests()
|
|
404
411
|
all_paths = [p.replace("\\", "/") for p in flatten_file_tree(file_tree)]
|
|
405
412
|
|
|
413
|
+
# Warn when Java project has no Mapper.xml — suggests files below scan depth.
|
|
414
|
+
_mybatis_warning: dict | None = None
|
|
415
|
+
if _is_java and not any(p.endswith("Mapper.xml") for p in all_paths):
|
|
416
|
+
_mybatis_warning = {
|
|
417
|
+
"area": "mybatis",
|
|
418
|
+
"reason": "Mapper XML files may exist below scan depth. Re-run with --depth 12.",
|
|
419
|
+
"impact": "high",
|
|
420
|
+
}
|
|
421
|
+
|
|
406
422
|
# ── 2. Detect stacks + entry points ───────────────────────────────
|
|
407
423
|
from dataclasses import replace as _replace
|
|
408
424
|
from sourcecode.detectors import ProjectDetector, build_default_detectors
|
|
@@ -500,6 +516,7 @@ class TaskContextBuilder:
|
|
|
500
516
|
code_notes_summary: Optional[dict[str, Any]] = None
|
|
501
517
|
suspected_areas: list[str] = []
|
|
502
518
|
improvement_opportunities: list[str] = []
|
|
519
|
+
cn_notes_for_ranking: list = []
|
|
503
520
|
|
|
504
521
|
if spec.enable_code_notes:
|
|
505
522
|
from dataclasses import asdict
|
|
@@ -507,6 +524,7 @@ class TaskContextBuilder:
|
|
|
507
524
|
|
|
508
525
|
cn_notes, _cn_adrs, cn_summary = CodeNotesAnalyzer().analyze(self.root)
|
|
509
526
|
code_notes_summary = asdict(cn_summary)
|
|
527
|
+
cn_notes_for_ranking = cn_notes
|
|
510
528
|
|
|
511
529
|
if task_name == "fix-bug":
|
|
512
530
|
bug_kinds = {"FIXME", "BUG", "HACK", "XXX"}
|
|
@@ -555,6 +573,7 @@ class TaskContextBuilder:
|
|
|
555
573
|
monorepo_packages=sm.monorepo_packages if sm.monorepo_packages else None,
|
|
556
574
|
git_hotspots=git_hotspots,
|
|
557
575
|
uncommitted_files=uncommitted_files,
|
|
576
|
+
code_notes=cn_notes_for_ranking if cn_notes_for_ranking else None,
|
|
558
577
|
)
|
|
559
578
|
|
|
560
579
|
# ── 7. Test gaps (generate-tests only) ────────────────────────────
|
|
@@ -594,6 +613,8 @@ class TaskContextBuilder:
|
|
|
594
613
|
conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
|
|
595
614
|
confidence = conf_summary.overall
|
|
596
615
|
gaps = [g.reason for g in analysis_gaps]
|
|
616
|
+
if _mybatis_warning:
|
|
617
|
+
gaps.append(_mybatis_warning["reason"])
|
|
597
618
|
|
|
598
619
|
# ── 9. why_these_files ────────────────────────────────────────────────
|
|
599
620
|
why_these_files: dict[str, str] = {
|
|
@@ -705,6 +726,7 @@ class TaskContextBuilder:
|
|
|
705
726
|
monorepo_packages: Optional[list] = None,
|
|
706
727
|
git_hotspots: Optional[dict[str, int]] = None,
|
|
707
728
|
uncommitted_files: Optional[set[str]] = None,
|
|
729
|
+
code_notes: Optional[list] = None,
|
|
708
730
|
) -> list[RelevantFile]:
|
|
709
731
|
from sourcecode.ranking_engine import RankingEngine
|
|
710
732
|
from sourcecode.file_classifier import FileClassifier
|
|
@@ -719,6 +741,35 @@ class TaskContextBuilder:
|
|
|
719
741
|
_uncommitted = uncommitted_files or set()
|
|
720
742
|
_max_churn = max(_hotspots.values(), default=1)
|
|
721
743
|
|
|
744
|
+
# Pre-compute fix-bug signals (used only when task_name == "fix-bug")
|
|
745
|
+
_annotated_files: set[str] = set()
|
|
746
|
+
_dominant_stack = ""
|
|
747
|
+
_recently_changed_stacks: set[str] = set()
|
|
748
|
+
if task_name == "fix-bug":
|
|
749
|
+
_bug_kinds = {"FIXME", "BUG"}
|
|
750
|
+
for _n in (code_notes or []):
|
|
751
|
+
if getattr(_n, "kind", "").upper() in _bug_kinds:
|
|
752
|
+
_annotated_files.add(getattr(_n, "path", ""))
|
|
753
|
+
|
|
754
|
+
def _file_stack(p: str) -> str:
|
|
755
|
+
ext = Path(p).suffix.lower()
|
|
756
|
+
if ext == ".java": return "java"
|
|
757
|
+
if ext in (".ts", ".tsx", ".js", ".jsx", ".mjs"): return "typescript"
|
|
758
|
+
if ext == ".py": return "python"
|
|
759
|
+
if ext == ".go": return "go"
|
|
760
|
+
if ext in (".kt", ".kts"): return "kotlin"
|
|
761
|
+
if ext == ".rs": return "rust"
|
|
762
|
+
if ext == ".rb": return "ruby"
|
|
763
|
+
return "unknown"
|
|
764
|
+
|
|
765
|
+
from collections import Counter as _Counter
|
|
766
|
+
_stk_counts: _Counter[str] = _Counter(
|
|
767
|
+
_file_stack(f) for f in _uncommitted if _file_stack(f) != "unknown"
|
|
768
|
+
)
|
|
769
|
+
if _stk_counts:
|
|
770
|
+
_dominant_stack = _stk_counts.most_common(1)[0][0]
|
|
771
|
+
_recently_changed_stacks = set(_stk_counts.keys())
|
|
772
|
+
|
|
722
773
|
scored: list[tuple[float, str, RelevantFile]] = []
|
|
723
774
|
|
|
724
775
|
for path in all_paths:
|
|
@@ -762,10 +813,28 @@ class TaskContextBuilder:
|
|
|
762
813
|
|
|
763
814
|
# Task-specific boosts for differentiated file weighting
|
|
764
815
|
path_lower = path.lower()
|
|
816
|
+
_fix_bug_why = ""
|
|
765
817
|
if task_name == "fix-bug":
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
818
|
+
_why_parts: list[str] = []
|
|
819
|
+
if path in _uncommitted:
|
|
820
|
+
content_boost += 0.40
|
|
821
|
+
_why_parts.append("uncommitted change (+0.40)")
|
|
822
|
+
_recency = min(0.30, _hotspots.get(path, 0) * 0.05)
|
|
823
|
+
if _recency > 0:
|
|
824
|
+
content_boost += _recency
|
|
825
|
+
_why_parts.append(f"recent commits (+{_recency:.2f})")
|
|
826
|
+
if path in _annotated_files:
|
|
827
|
+
content_boost += 0.20
|
|
828
|
+
_why_parts.append("FIXME/BUG annotation (+0.20)")
|
|
829
|
+
_file_stk = _file_stack(path)
|
|
830
|
+
if _dominant_stack and _file_stk == _dominant_stack:
|
|
831
|
+
content_boost += 0.10
|
|
832
|
+
_why_parts.append("dominant changed stack (+0.10)")
|
|
833
|
+
if _recently_changed_stacks and _file_stk not in _recently_changed_stacks and _file_stk != "unknown":
|
|
834
|
+
content_boost -= 0.30
|
|
835
|
+
_why_parts.append("different stack from recent changes (-0.30)")
|
|
836
|
+
if _why_parts:
|
|
837
|
+
_fix_bug_why = ", ".join(_why_parts)
|
|
769
838
|
elif task_name == "generate-tests":
|
|
770
839
|
stem = Path(path).stem.lower()
|
|
771
840
|
has_test = any(
|
|
@@ -797,7 +866,7 @@ class TaskContextBuilder:
|
|
|
797
866
|
)
|
|
798
867
|
all_reasons = [r for r in fs.reasons if r != "source file"] + content_reasons
|
|
799
868
|
reason_str = ", ".join(all_reasons) if all_reasons else "source file"
|
|
800
|
-
why_str = _java_why(path, file_class)
|
|
869
|
+
why_str = _fix_bug_why if _fix_bug_why else _java_why(path, file_class)
|
|
801
870
|
|
|
802
871
|
scored.append((total, path, RelevantFile(
|
|
803
872
|
path=path,
|