sourcecode 1.30.1__tar.gz → 1.30.3__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.30.3/.continue-here.md +177 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/PKG-INFO +3 -3
- {sourcecode-1.30.1 → sourcecode-1.30.3}/README.md +2 -2
- {sourcecode-1.30.1 → sourcecode-1.30.3}/pyproject.toml +1 -1
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/cli.py +2 -0
- sourcecode-1.30.3/src/sourcecode/flow_analyzer.py +310 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/prepare_context.py +13 -0
- sourcecode-1.30.1/.continue-here.md +0 -129
- {sourcecode-1.30.1 → sourcecode-1.30.3}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/.gitignore +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/.ruff.toml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/CONTRIBUTING.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/LICENSE +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/SECURITY.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/docs/privacy.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/docs/schema.md +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/raw +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/run_cli.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/__init__.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/conftest.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_classifier.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_cli.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_python.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_encoding_regression.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_packaging.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_real_projects.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_redactor.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_scanner.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_schema.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_summarizer.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_telemetry.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_v1_10_regressions.py +0 -0
- {sourcecode-1.30.1 → sourcecode-1.30.3}/tests/test_workspace_analyzer.py +0 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Continue Here — atlas-cli session 7
|
|
2
|
+
|
|
3
|
+
**Paused:** 2026-05-16 (sesión 7)
|
|
4
|
+
**Repo:** `/Users/user/Downloads/atlas-cli`
|
|
5
|
+
**Branch:** master
|
|
6
|
+
**Último commit:** `7085e97` — (fix) corrigiendo prepare-context review-pr mejorando su representatividad y minimizando falsos positivos
|
|
7
|
+
**Working tree:** LIMPIO
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Lo que se hizo esta sesión
|
|
12
|
+
|
|
13
|
+
### BFS over-expansion fix — 3 fixes semánticos en `prepare_context.py`
|
|
14
|
+
|
|
15
|
+
**Problema de partida** (heredado sesión 6): `review-pr` con 2 archivos cambiados (`User.java`, `ArticleFavorite.java`) devolvía 24 `relevant_files` incluyendo todos los controllers, toda la capa security, GraphQL completo.
|
|
16
|
+
|
|
17
|
+
**Diagnóstico confirmado:**
|
|
18
|
+
- `_classify_changed_file` es path-only, sin inspección de diff → worst-case siempre
|
|
19
|
+
- BFS repo-wide sin constraint de módulo → todo lo que importa `User` incluido
|
|
20
|
+
- Sin change severity classifier → getter renombrado = campo eliminado = mismo BFS
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
### Fix 1 — BFS module constraint
|
|
25
|
+
|
|
26
|
+
Dentro del loop BFS (`_hop_dep_map`), antes de añadir a `_hop_bfs_staged`:
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
_is_cross_module = bool(_dep_module) and _dep_module not in affected_modules_set
|
|
30
|
+
if _is_cross_module:
|
|
31
|
+
_seed_expansion = _EXPANSION_TARGETS.get(_seed_atype, frozenset())
|
|
32
|
+
if _seed_severity == "security_change":
|
|
33
|
+
_seed_expansion = _seed_expansion | frozenset({"security", "spring_config", "config"})
|
|
34
|
+
if _hop_num >= 2 or _dep_atype not in _seed_expansion:
|
|
35
|
+
continue
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Regla: cross-module solo en hop-1 si dep_atype ∈ `_EXPANSION_TARGETS[seed]`. Hop-2+: cross-module bloqueado siempre.
|
|
39
|
+
|
|
40
|
+
### Fix 2 — Cross-module score penalty
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
_cross_module_factor = 0.60 if _is_cross_module else 1.0
|
|
44
|
+
_dep_score = round(_dep_score_base * (0.70 ** _hop_num) * _cross_module_factor, 2)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Same-module files ganan prioridad en el per-hop cap (8/6/4).
|
|
48
|
+
|
|
49
|
+
### Fix 3 — `_classify_diff_severity` método nuevo
|
|
50
|
+
|
|
51
|
+
Nuevo método que lee `git diff` y clasifica semánticamente:
|
|
52
|
+
- `trivial` → no siembra BFS (solo comentarios/whitespace)
|
|
53
|
+
- `field_change` → solo hop-1; deps excluidos del frontier hop-2+
|
|
54
|
+
- `api_change` → BFS completo
|
|
55
|
+
- `security_change` → BFS completo + security chain cross-module permitido
|
|
56
|
+
- `unknown` → tratado como api_change (safe default)
|
|
57
|
+
|
|
58
|
+
Integración en `_build_delta_impact`:
|
|
59
|
+
- Step 1b: `diff_severities = {f: self._classify_diff_severity(f, since) for f in changed_files}`
|
|
60
|
+
- Frontier gate: `trivial` files excluidos del BFS frontier
|
|
61
|
+
- Loop gate: `field_change` seeds excluidos del hop-2+ frontier (`_hop_scored` no actualizado)
|
|
62
|
+
- `_seed_severity` computado por seed para aplicar gates correctamente
|
|
63
|
+
|
|
64
|
+
**Bug encontrado durante debug:** `_SECURITY` regex original incluía "password", "role", "permission" — field names comunes en domain models. `this.password = password` en `User.java` disparaba `security_change` falso positivo. Fix: removidos de la regex para Java/TS/Python. Mantenidos: `jwt|auth|token|credential|encrypt|decrypt|oauth|saml|ldap|principal|Security`.
|
|
65
|
+
|
|
66
|
+
**Nota sobre instalación:** `sourcecode` CLI usa pipx venv separado. Para testear source editable:
|
|
67
|
+
```bash
|
|
68
|
+
/Users/user/.local/pipx/venvs/sourcecode/bin/python -m pip install -e .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Resultado smoke test (spring-boot-realworld-example-app)
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
changed_files: User.java, ArticleFavorite.java
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
| Métrica | Sesión 6 | Sesión 7 |
|
|
80
|
+
|---------|----------|----------|
|
|
81
|
+
| `relevant_files` total | 14 | **4** |
|
|
82
|
+
| Controllers REST | 5 | **0** |
|
|
83
|
+
| Security chain | 3 | **0** |
|
|
84
|
+
| GraphQL | 0 | **0** |
|
|
85
|
+
| Tests | 0 | **0** |
|
|
86
|
+
| Same-module repos | 2 | 2 ✓ |
|
|
87
|
+
|
|
88
|
+
Los 4 archivos resultantes:
|
|
89
|
+
- `User.java` (changed)
|
|
90
|
+
- `ArticleFavorite.java` (changed)
|
|
91
|
+
- `UserRepository.java` (Step 3 same-module expansion)
|
|
92
|
+
- `ArticleFavoriteRepository.java` (Step 3 same-module expansion)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Estado de tests al pausar
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
771 passed, 3 skipped
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Pre-existing failures (excluidos del run):
|
|
103
|
+
- `test_block2_coverage.py::test_java_marked_unsupported` — DocRecord bug en doc_analyzer.py
|
|
104
|
+
- `test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile_keeps_declared_versions` — typer version mismatch
|
|
105
|
+
- `test_packaging.py::test_console_script_reports_version` — versión instalada vs source
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Decisiones técnicas de esta sesión
|
|
110
|
+
|
|
111
|
+
- **`_EXPANSION_TARGETS` reutilizado en BFS gate**: misma tabla que Step 3, coherente con semántica del sistema
|
|
112
|
+
- **hop-2+ cross-module siempre bloqueado**: excepto security_change que permite security chain
|
|
113
|
+
- **"password" removido de `_SECURITY` en `_classify_diff_severity`**: distingue field name de auth logic real
|
|
114
|
+
- **Cross-module penalty 0.60**: empírico, asegura que same-module gana el per-hop cap
|
|
115
|
+
- **`_bfs_seen.add` antes del gate**: correcto — prevents double-processing aunque archivo sea filtrado
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Pendiente / próximos pasos
|
|
120
|
+
|
|
121
|
+
1. **Smoke test `delta`** — verificar Fix 1-3 no afecta el comando `delta`:
|
|
122
|
+
```bash
|
|
123
|
+
cd ~/Documents/workspace/spring-boot-realworld-example-app
|
|
124
|
+
sourcecode prepare-context delta . --since HEAD~1 2>/dev/null | python3 -c "
|
|
125
|
+
import json,sys; d=json.load(sys.stdin)
|
|
126
|
+
print('relevant_files:', len(d.get('relevant_files', [])))
|
|
127
|
+
"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
2. **Smoke test `security_change` path** — verificar que cambio real en auth logic SÍ expande security chain:
|
|
131
|
+
- Modificar temporalmente `JwtService.java` (añadir línea con "jwt" o "auth")
|
|
132
|
+
- Correr `review-pr` → debería incluir AuthorizationService, JwtTokenFilter
|
|
133
|
+
|
|
134
|
+
3. **Tests unitarios para `_classify_diff_severity`** — actualmente sin tests. Casos: trivial (solo comentarios), field_change (field declaration), api_change (import removal), security_change (jwt en diff), unknown (no diff).
|
|
135
|
+
|
|
136
|
+
4. **`User.java` clasificado como `"source"` no `"domain_model"`** — "user" stem no está en `_ENTITY_KW` ni en `_FOLDER_TYPE_MAP`. Fix opcional: añadir "user", "profile", "account" a detección domain_model.
|
|
137
|
+
|
|
138
|
+
5. **Smoke tests saint-server** (sesiones anteriores) — pendiente desde sesión 4/5.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Para retomar
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
cd /Users/user/Downloads/atlas-cli
|
|
146
|
+
git log --oneline -3
|
|
147
|
+
# debe mostrar:
|
|
148
|
+
# 7085e97 (fix) corrigiendo prepare-context review-pr mejorando su representatividad...
|
|
149
|
+
# 772a01e (fix) corrigiendo bug que identificaba falsos positivos en review-pr
|
|
150
|
+
# 5c06ef3 (feature) implementando mejoras significativas en prepare-context review-pr
|
|
151
|
+
|
|
152
|
+
# Tests
|
|
153
|
+
python3 -m pytest tests/ \
|
|
154
|
+
--ignore=tests/test_block2_coverage.py \
|
|
155
|
+
--ignore=tests/test_packaging.py \
|
|
156
|
+
--deselect=tests/test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile_keeps_declared_versions \
|
|
157
|
+
-q
|
|
158
|
+
# Expected: 771 passed, 3 skipped
|
|
159
|
+
|
|
160
|
+
# Reinstall en pipx venv para smoke tests
|
|
161
|
+
/Users/user/.local/pipx/venvs/sourcecode/bin/python -m pip install -e . -q
|
|
162
|
+
|
|
163
|
+
# Smoke test review-pr
|
|
164
|
+
cd ~/Documents/workspace/spring-boot-realworld-example-app
|
|
165
|
+
sourcecode prepare-context review-pr . 2>/dev/null | python3 -c "
|
|
166
|
+
import json, sys
|
|
167
|
+
d = json.load(sys.stdin)
|
|
168
|
+
print('relevant_files:', len(d.get('relevant_files', [])))
|
|
169
|
+
for f in d.get('relevant_files', []):
|
|
170
|
+
print(' ', f.get('score'), f.get('path').split('/')[-1])
|
|
171
|
+
"
|
|
172
|
+
# Expected: 4 files, no controllers, no security, no GraphQL
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
*Pausado 2026-05-16 sesión 7 — gsd:pause-work*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.30.
|
|
3
|
+
Version: 1.30.3
|
|
4
4
|
Summary: Deterministic codebase context for AI coding agents
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -221,7 +221,7 @@ Description-Content-Type: text/markdown
|
|
|
221
221
|
|
|
222
222
|
**Compressed AI-ready context for Java/Spring enterprise codebases.**
|
|
223
223
|
|
|
224
|
-

|
|
225
225
|

|
|
226
226
|
|
|
227
227
|
---
|
|
@@ -255,7 +255,7 @@ pipx install sourcecode
|
|
|
255
255
|
|
|
256
256
|
```bash
|
|
257
257
|
sourcecode version
|
|
258
|
-
# sourcecode 1.30.
|
|
258
|
+
# sourcecode 1.30.3
|
|
259
259
|
```
|
|
260
260
|
|
|
261
261
|
---
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Compressed AI-ready context for Java/Spring enterprise codebases.**
|
|
4
4
|
|
|
5
|
-

|
|
6
6
|

|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -36,7 +36,7 @@ pipx install sourcecode
|
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
sourcecode version
|
|
39
|
-
# sourcecode 1.30.
|
|
39
|
+
# sourcecode 1.30.3
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
---
|
|
@@ -1866,6 +1866,8 @@ def prepare_context_cmd(
|
|
|
1866
1866
|
out["review_hotspots"] = output.review_hotspots
|
|
1867
1867
|
if output.suggested_review_order:
|
|
1868
1868
|
out["suggested_review_order"] = output.suggested_review_order
|
|
1869
|
+
if output.execution_paths:
|
|
1870
|
+
out["execution_paths"] = output.execution_paths
|
|
1869
1871
|
if output.impact_summary:
|
|
1870
1872
|
out["impact_summary"] = output.impact_summary
|
|
1871
1873
|
if output.why_these_files:
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""flow_analyzer.py — Evidence-based execution path extraction for PR context.
|
|
2
|
+
|
|
3
|
+
Builds Entry → Service → Repository → EndState ordered sequences using ONLY
|
|
4
|
+
direct code evidence: field injection, constructor params, type annotations,
|
|
5
|
+
method calls, explicit instantiation.
|
|
6
|
+
|
|
7
|
+
V3: execution_paths with runtime_notes — conditional branches, optional execution,
|
|
8
|
+
and async side-effects are surfaced when explicit code signals exist.
|
|
9
|
+
No inference, no naming, no invented behavior.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Callable, Optional
|
|
16
|
+
|
|
17
|
+
_ENTRY_ARTIFACT_TYPES = frozenset({"controller", "entrypoint"})
|
|
18
|
+
_SERVICE_ARTIFACT_TYPES = frozenset({"service"})
|
|
19
|
+
_REPO_ARTIFACT_TYPES = frozenset({"repository", "mapper"})
|
|
20
|
+
|
|
21
|
+
_DB_KEYWORDS = frozenset({"repository", "dao", "mapper", "store", "jpa", "jdbc", "sql"})
|
|
22
|
+
_EVENT_KEYWORDS = frozenset({"event", "publish", "emit", "kafka", "queue", "rabbit", "sns", "bus"})
|
|
23
|
+
|
|
24
|
+
_HTTP_ENTRY_RE = re.compile(
|
|
25
|
+
r'@(?:Get|Post|Put|Delete|Patch|Request)Mapping[^)]*\)'
|
|
26
|
+
r'|@(?:Get|Post|Put|Delete|Patch)\([^)]*\)'
|
|
27
|
+
r'|@\w+\.(?:get|post|put|delete|patch)\([^)]*\)',
|
|
28
|
+
re.IGNORECASE,
|
|
29
|
+
)
|
|
30
|
+
_METHOD_NAME_RE = re.compile(
|
|
31
|
+
r'(?:public\s+|async\s+|def\s+|function\s+)*'
|
|
32
|
+
r'(?:[\w<>\[\]]+\s+)?'
|
|
33
|
+
r'(\w+)\s*\(',
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Runtime signal patterns: (compiled_regex, note_text)
|
|
37
|
+
# Only signals with explicit code evidence — no inference.
|
|
38
|
+
# Three categories: condition | branch | async
|
|
39
|
+
_RUNTIME_SIGNALS: list[tuple[re.Pattern, str]] = [
|
|
40
|
+
# ── Conditional / auth guards ─────────────────────────────────────────────
|
|
41
|
+
(re.compile(r'@PreAuthorize|@Secured|@RolesAllowed', re.IGNORECASE),
|
|
42
|
+
"condition: authorization check present (@PreAuthorize / @Secured)"),
|
|
43
|
+
(re.compile(r'isAuthenticated\(\)|hasRole\(|hasAuthority\(|SecurityContextHolder', re.IGNORECASE),
|
|
44
|
+
"condition: reads authentication context"),
|
|
45
|
+
(re.compile(r'featureFlag|FeatureToggle|\.isEnabled\s*\(|\.isActive\s*\(', re.IGNORECASE),
|
|
46
|
+
"condition: feature flag gates execution"),
|
|
47
|
+
# Null/empty guard with early return — matches if (...null/empty...) return/throw on same line
|
|
48
|
+
(re.compile(r'if\s*\([^)]*(?:==\s*null|!=\s*null|isEmpty\s*\(\)|isBlank\s*\(\))[^)]*\)'
|
|
49
|
+
r'\s*(?:\{?\s*)?(?:return|throw)\b', re.IGNORECASE),
|
|
50
|
+
"condition: null/empty guard with early return"),
|
|
51
|
+
|
|
52
|
+
# ── Optional execution / branching ────────────────────────────────────────
|
|
53
|
+
(re.compile(r'@Cacheable|@CacheEvict|@CachePut', re.IGNORECASE),
|
|
54
|
+
"branch: Spring cache may short-circuit downstream call"),
|
|
55
|
+
(re.compile(r'\.getIfPresent\s*\(|cache\.get\s*\(|cacheManager\.', re.IGNORECASE),
|
|
56
|
+
"branch: manual cache lookup may short-circuit"),
|
|
57
|
+
(re.compile(r'Optional\s*<|\.orElseThrow\s*\(|\.orElseGet\s*\(|\.orElse\s*\(', re.IGNORECASE),
|
|
58
|
+
"branch: result may be absent (Optional)"),
|
|
59
|
+
|
|
60
|
+
# ── Async / side effects ──────────────────────────────────────────────────
|
|
61
|
+
(re.compile(r'@Async\b'),
|
|
62
|
+
"async: runs in separate thread (@Async)"),
|
|
63
|
+
(re.compile(r'CompletableFuture|\.supplyAsync\s*\(|\.runAsync\s*\('),
|
|
64
|
+
"async: non-blocking future-based execution"),
|
|
65
|
+
(re.compile(r'\basync\s+def\b|\bawait\b', re.IGNORECASE),
|
|
66
|
+
"async: non-blocking (async/await)"),
|
|
67
|
+
(re.compile(r'publishEvent\s*\(|applicationEventPublisher|eventPublisher\.', re.IGNORECASE),
|
|
68
|
+
"async: Spring application event emitted"),
|
|
69
|
+
(re.compile(r'kafkaTemplate\.|KafkaProducer|@KafkaListener', re.IGNORECASE),
|
|
70
|
+
"async: Kafka message produced"),
|
|
71
|
+
(re.compile(r'rabbitTemplate\.|amqpTemplate\.|@RabbitListener', re.IGNORECASE),
|
|
72
|
+
"async: RabbitMQ message sent"),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _detect_lang(path: str) -> str:
|
|
77
|
+
return {
|
|
78
|
+
".java": "java", ".kt": "kotlin",
|
|
79
|
+
".py": "python",
|
|
80
|
+
".ts": "typescript", ".tsx": "typescript",
|
|
81
|
+
".js": "javascript", ".jsx": "javascript",
|
|
82
|
+
".go": "go", ".cs": "csharp", ".rb": "ruby", ".php": "php",
|
|
83
|
+
}.get(Path(path).suffix.lower(), "unknown")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _strip_comments(content: str, lang: str) -> str:
|
|
87
|
+
content = re.sub(r"/\*.*?\*/", " ", content, flags=re.DOTALL)
|
|
88
|
+
content = re.sub(r"//[^\n]*", " ", content)
|
|
89
|
+
if lang in ("python", "ruby", "go"):
|
|
90
|
+
content = re.sub(r"#[^\n]*", " ", content)
|
|
91
|
+
return content
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _read_safe(root: Path, rel_path: str) -> str:
|
|
95
|
+
try:
|
|
96
|
+
return (root / rel_path).read_text(encoding="utf-8", errors="ignore")
|
|
97
|
+
except (OSError, ValueError):
|
|
98
|
+
return ""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _collect_runtime_notes(content: str, lang: str) -> list[str]:
|
|
102
|
+
"""Scan comment-stripped content for explicit runtime behavior signals.
|
|
103
|
+
|
|
104
|
+
Returns only notes backed by a direct code pattern match.
|
|
105
|
+
Returns [] when no signals are found.
|
|
106
|
+
"""
|
|
107
|
+
clean = _strip_comments(content, lang)
|
|
108
|
+
notes: list[str] = []
|
|
109
|
+
seen: set[str] = set()
|
|
110
|
+
for pattern, note in _RUNTIME_SIGNALS:
|
|
111
|
+
if note not in seen and pattern.search(clean):
|
|
112
|
+
notes.append(note)
|
|
113
|
+
seen.add(note)
|
|
114
|
+
return notes
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _find_entry_method(clean: str) -> Optional[str]:
|
|
118
|
+
m = _HTTP_ENTRY_RE.search(clean)
|
|
119
|
+
if not m:
|
|
120
|
+
return None
|
|
121
|
+
after = clean[m.end():]
|
|
122
|
+
mn = _METHOD_NAME_RE.match(after.lstrip())
|
|
123
|
+
if mn:
|
|
124
|
+
name = mn.group(1)
|
|
125
|
+
if name.lower() not in ("public", "async", "def", "function", "void", "override"):
|
|
126
|
+
return name
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _build_field_map(clean: str) -> dict[str, str]:
|
|
131
|
+
"""Map field_name_lower → ClassName from injection patterns."""
|
|
132
|
+
fmap: dict[str, str] = {}
|
|
133
|
+
for m in re.finditer(r"private\s+(\w+)(?:<[^>]+>)?\s+(\w+)\s*[;=,)]", clean):
|
|
134
|
+
fmap[m.group(2).lower()] = m.group(1)
|
|
135
|
+
for m in re.finditer(r"(?:private|protected|readonly)\s+(\w+)\s*:\s*(\w+)", clean):
|
|
136
|
+
fmap[m.group(1).lower()] = m.group(2)
|
|
137
|
+
for m in re.finditer(r"self\.(\w+)\s*=\s*(\w+)\s*\(", clean):
|
|
138
|
+
fmap[m.group(1).lower()] = m.group(2)
|
|
139
|
+
return fmap
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _find_called_method(clean: str, class_name: str, fmap: dict[str, str]) -> Optional[str]:
|
|
143
|
+
fields = [f for f, t in fmap.items() if t.lower() == class_name.lower()]
|
|
144
|
+
for field in fields:
|
|
145
|
+
pat = rf"\bthis\.{re.escape(field)}\.(\w+)\s*\(|\b{re.escape(field)}\.(\w+)\s*\("
|
|
146
|
+
for m in re.finditer(pat, clean, re.IGNORECASE):
|
|
147
|
+
name = m.group(1) or m.group(2)
|
|
148
|
+
if name and name.lower() not in ("class", "new", "super", "get", "set"):
|
|
149
|
+
return name
|
|
150
|
+
for m in re.finditer(rf"\b{re.escape(class_name)}\.(\w+)\s*\(", clean, re.IGNORECASE):
|
|
151
|
+
name = m.group(1)
|
|
152
|
+
if name.lower() not in ("class", "new", "super"):
|
|
153
|
+
return name
|
|
154
|
+
return None
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _has_code_evidence(clean: str, class_name: str) -> bool:
|
|
158
|
+
"""True only when class_name has direct code evidence in pre-stripped content."""
|
|
159
|
+
esc = re.escape(class_name)
|
|
160
|
+
if re.search(rf"\b(?:private|protected)\s+{esc}\b", clean, re.IGNORECASE):
|
|
161
|
+
return True
|
|
162
|
+
if re.search(rf"[,(]\s*{esc}\s+\w+", clean, re.IGNORECASE):
|
|
163
|
+
return True
|
|
164
|
+
if re.search(rf":\s*{esc}\b", clean, re.IGNORECASE):
|
|
165
|
+
return True
|
|
166
|
+
if re.search(rf"\bnew\s+{esc}\s*\(", clean, re.IGNORECASE):
|
|
167
|
+
return True
|
|
168
|
+
if re.search(rf"\b{esc}\s*\(", clean):
|
|
169
|
+
return True
|
|
170
|
+
if re.search(rf"\b{esc}\b", clean, re.IGNORECASE):
|
|
171
|
+
non_import = re.search(
|
|
172
|
+
rf"^(?!\s*(?:import|require|from|//|#|\*)\b).*\b{esc}\b",
|
|
173
|
+
clean, re.IGNORECASE | re.MULTILINE,
|
|
174
|
+
)
|
|
175
|
+
if non_import:
|
|
176
|
+
return True
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _find_evidenced_ordered(
|
|
181
|
+
root: Path,
|
|
182
|
+
source_path: str,
|
|
183
|
+
candidates: list[str],
|
|
184
|
+
) -> list[tuple[str, Optional[str]]]:
|
|
185
|
+
"""Return (class_name, method_or_None) for candidates with direct code evidence,
|
|
186
|
+
ordered by their first appearance position in the source file."""
|
|
187
|
+
content = _read_safe(root, source_path)
|
|
188
|
+
if not content:
|
|
189
|
+
return []
|
|
190
|
+
lang = _detect_lang(source_path)
|
|
191
|
+
clean = _strip_comments(content, lang)
|
|
192
|
+
fmap = _build_field_map(clean)
|
|
193
|
+
|
|
194
|
+
positioned: list[tuple[int, str, Optional[str]]] = []
|
|
195
|
+
for cand_path in candidates:
|
|
196
|
+
class_name = Path(cand_path).stem
|
|
197
|
+
if not _has_code_evidence(clean, class_name):
|
|
198
|
+
continue
|
|
199
|
+
method = _find_called_method(clean, class_name, fmap)
|
|
200
|
+
m = re.search(rf"\b{re.escape(class_name)}\b", clean, re.IGNORECASE)
|
|
201
|
+
pos = m.start() if m else len(clean)
|
|
202
|
+
positioned.append((pos, class_name, method))
|
|
203
|
+
|
|
204
|
+
positioned.sort(key=lambda x: x[0])
|
|
205
|
+
return [(cls, meth) for _, cls, meth in positioned]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _detect_end_state(path: list[str]) -> str:
|
|
209
|
+
for step in path:
|
|
210
|
+
s = step.lower()
|
|
211
|
+
if any(kw in s for kw in _DB_KEYWORDS):
|
|
212
|
+
return "DB write"
|
|
213
|
+
if any(kw in s for kw in _EVENT_KEYWORDS):
|
|
214
|
+
return "event emitted"
|
|
215
|
+
return "HTTP response"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _step_label(class_name: str, method: Optional[str]) -> str:
|
|
219
|
+
return f"{class_name}.{method}" if method else class_name
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def _path_name(entry_class: str) -> str:
|
|
223
|
+
domain = re.sub(
|
|
224
|
+
r"(?:RestController|Controller|Resource|Handler|Api|Endpoint|Router|Servlet)$",
|
|
225
|
+
"", entry_class, flags=re.IGNORECASE,
|
|
226
|
+
)
|
|
227
|
+
return re.sub(r"(?<=[a-z])(?=[A-Z])", " ", domain).strip()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def analyze_execution_paths(
|
|
231
|
+
changed_files: list[str],
|
|
232
|
+
all_paths: list[str],
|
|
233
|
+
root: Path,
|
|
234
|
+
classify_fn: Callable[[str], dict],
|
|
235
|
+
max_paths: int = 3,
|
|
236
|
+
) -> list[dict]:
|
|
237
|
+
"""Build ordered execution paths with runtime behavior signals.
|
|
238
|
+
|
|
239
|
+
Each path:
|
|
240
|
+
- One service per entry point (most evident, earliest-referenced)
|
|
241
|
+
- Each step requires direct code evidence
|
|
242
|
+
- runtime_notes populated from explicit code signals only (never inferred)
|
|
243
|
+
- Forward-only: Controller → Service → Repository
|
|
244
|
+
|
|
245
|
+
Returns list of: {name, entry_point, path, runtime_notes, end_state}
|
|
246
|
+
Returns [] when no verifiable path exists.
|
|
247
|
+
"""
|
|
248
|
+
entry_files = [
|
|
249
|
+
f for f in changed_files
|
|
250
|
+
if classify_fn(f)["artifact_type"] in _ENTRY_ARTIFACT_TYPES
|
|
251
|
+
]
|
|
252
|
+
if not entry_files:
|
|
253
|
+
return []
|
|
254
|
+
|
|
255
|
+
all_services = [p for p in all_paths if classify_fn(p)["artifact_type"] in _SERVICE_ARTIFACT_TYPES]
|
|
256
|
+
all_repos = [p for p in all_paths if classify_fn(p)["artifact_type"] in _REPO_ARTIFACT_TYPES]
|
|
257
|
+
|
|
258
|
+
result: list[dict] = []
|
|
259
|
+
|
|
260
|
+
for entry_path in entry_files[:max_paths]:
|
|
261
|
+
entry_class = Path(entry_path).stem
|
|
262
|
+
lang = _detect_lang(entry_path)
|
|
263
|
+
|
|
264
|
+
entry_content = _read_safe(root, entry_path)
|
|
265
|
+
entry_clean = _strip_comments(entry_content, lang) if entry_content else ""
|
|
266
|
+
entry_method = _find_entry_method(entry_clean) if entry_clean else None
|
|
267
|
+
entry_point_str = _step_label(entry_class, entry_method)
|
|
268
|
+
|
|
269
|
+
evidenced_svcs = _find_evidenced_ordered(root, entry_path, all_services)
|
|
270
|
+
if not evidenced_svcs:
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
svc_class, svc_method = evidenced_svcs[0]
|
|
274
|
+
svc_label = _step_label(svc_class, svc_method)
|
|
275
|
+
|
|
276
|
+
svc_path = next((p for p in all_services if Path(p).stem == svc_class), None)
|
|
277
|
+
svc_content = _read_safe(root, svc_path) if svc_path else ""
|
|
278
|
+
svc_lang = _detect_lang(svc_path) if svc_path else "unknown"
|
|
279
|
+
|
|
280
|
+
# Service step — notes scoped to service file only
|
|
281
|
+
path_items: list[dict] = [
|
|
282
|
+
{"step": svc_label,
|
|
283
|
+
"notes": _collect_runtime_notes(svc_content, svc_lang) if svc_content else []},
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
# Repository step — notes scoped to repo file only
|
|
287
|
+
if svc_path:
|
|
288
|
+
evidenced_repos = _find_evidenced_ordered(root, svc_path, all_repos)
|
|
289
|
+
if evidenced_repos:
|
|
290
|
+
repo_class, repo_method = evidenced_repos[0]
|
|
291
|
+
repo_label = _step_label(repo_class, repo_method)
|
|
292
|
+
repo_path = next((p for p in all_repos if Path(p).stem == repo_class), None)
|
|
293
|
+
repo_content = _read_safe(root, repo_path) if repo_path else ""
|
|
294
|
+
repo_lang = _detect_lang(repo_path) if repo_path else "unknown"
|
|
295
|
+
path_items.append(
|
|
296
|
+
{"step": repo_label,
|
|
297
|
+
"notes": _collect_runtime_notes(repo_content, repo_lang) if repo_content else []},
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
# Entry-point notes scoped to controller file
|
|
301
|
+
entry_notes = _collect_runtime_notes(entry_content, lang) if entry_content else []
|
|
302
|
+
|
|
303
|
+
result.append({
|
|
304
|
+
"name": _path_name(entry_class),
|
|
305
|
+
"entry_point": {"step": entry_point_str, "notes": entry_notes},
|
|
306
|
+
"path": path_items,
|
|
307
|
+
"end_state": _detect_end_state([item["step"] for item in path_items]),
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
return result
|
|
@@ -351,6 +351,7 @@ class TaskOutput:
|
|
|
351
351
|
test_coverage_risk: dict = field(default_factory=dict)
|
|
352
352
|
review_hotspots: list[str] = field(default_factory=list)
|
|
353
353
|
suggested_review_order: list[str] = field(default_factory=list)
|
|
354
|
+
execution_paths: list[dict] = field(default_factory=list)
|
|
354
355
|
|
|
355
356
|
|
|
356
357
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -874,6 +875,17 @@ class TaskContextBuilder:
|
|
|
874
875
|
_pr_suggested_review_order.append(_f)
|
|
875
876
|
_seen_order.add(_f)
|
|
876
877
|
|
|
878
|
+
# ── 6d. review-pr: execution paths ──────────────────────────────────
|
|
879
|
+
_execution_paths: list[dict] = []
|
|
880
|
+
if task_name == "review-pr" and _delta_files:
|
|
881
|
+
from sourcecode.flow_analyzer import analyze_execution_paths
|
|
882
|
+
_execution_paths = analyze_execution_paths(
|
|
883
|
+
changed_files=sorted(_delta_files),
|
|
884
|
+
all_paths=all_paths,
|
|
885
|
+
root=self.root,
|
|
886
|
+
classify_fn=self._classify_changed_file,
|
|
887
|
+
)
|
|
888
|
+
|
|
877
889
|
# ── 6c. Symptom keyword boost + related notes (fix-bug + --symptom) ──
|
|
878
890
|
symptom_keywords: list[str] = []
|
|
879
891
|
related_notes: list[dict] = []
|
|
@@ -1104,6 +1116,7 @@ class TaskContextBuilder:
|
|
|
1104
1116
|
test_coverage_risk=_pr_test_coverage_risk,
|
|
1105
1117
|
review_hotspots=_pr_review_hotspots,
|
|
1106
1118
|
suggested_review_order=_pr_suggested_review_order,
|
|
1119
|
+
execution_paths=_execution_paths,
|
|
1107
1120
|
)
|
|
1108
1121
|
|
|
1109
1122
|
def render_prompt(self, output: TaskOutput) -> str:
|