sourcecode 1.27.0__tar.gz → 1.29.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.29.0/.continue-here.md +108 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/PKG-INFO +3 -3
- {sourcecode-1.27.0 → sourcecode-1.29.0}/README.md +2 -2
- {sourcecode-1.27.0 → sourcecode-1.29.0}/pyproject.toml +1 -1
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/cli.py +58 -1
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/prepare_context.py +175 -12
- sourcecode-1.29.0/tests/test_encoding_regression.py +138 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_v1_10_regressions.py +39 -13
- sourcecode-1.27.0/.continue-here.md +0 -173
- sourcecode-1.27.0/tests/test_encoding_regression.py +0 -62
- {sourcecode-1.27.0 → sourcecode-1.29.0}/.agents/skills/source-command-gsd-join-discord/SKILL.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/.agents/skills/source-command-gsd-review-backlog/SKILL.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/.agents/skills/source-command-gsd-workstreams/SKILL.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/.gitignore +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/.ruff.toml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/CONTRIBUTING.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/LICENSE +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/SECURITY.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/docs/privacy.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/docs/schema.md +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/raw +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/run_cli.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/__init__.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/conftest.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/coverage.xml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/fastapi_app/pyproject.toml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/fastapi_app/src/main.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/go_service/cmd/api/main.go +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/go_service/go.mod +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/jacoco.xml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/latin1_sample.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/latin1_sample_iso.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/lcov.info +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/nextjs_app/app/page.tsx +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/nextjs_app/package.json +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/nextjs_app/pnpm-lock.yaml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/pnpm_monorepo/apps/web/app/page.tsx +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/pnpm_monorepo/apps/web/package.json +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/pnpm_monorepo/packages/api/main.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/pnpm_monorepo/packages/api/pyproject.toml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/pnpm_monorepo/pnpm-workspace.yaml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/pom.xml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/application/service/FindAusenteService.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/domain/entities/Ausente.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/ausente/infrastructure/rest/AusenteRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/application/service/FindAutocoberturasService.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/domain/entities/Autocoberturas.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/autocoberturas/infrastructure/rest/AutocoberturasRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/application/service/FindCalendarioTrabajadorService.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/domain/entities/CalendarioTrabajador.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/calendarioTrabajador/infrastructure/rest/CalendarioTrabajadorRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/application/service/FindDepartamentoService.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/domain/entities/Departamento.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/departamento/infrastructure/rest/DepartamentoRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/application/service/FindEmpleadoService.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/domain/entities/Empleado.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/ddd/empleado/infrastructure/rest/EmpleadoRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/DemoApplication.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/config/FilterConfig.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/domain/Health.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/mapper/HealthMapper.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/repository/HealthRepository.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/service/HealthService.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/HealthRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/java/com/example/demo/web/NominaRestController.java +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application-dev.yml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/resources/application.yml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/fixtures/spring_boot_minimal/src/main/resources/mapper/HealthMapper.xml +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_architecture_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_architecture_summary.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_ast_extractor.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_block1_reliability.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_block2_coverage.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_block5_quality.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_bug_fixes_v16.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_classifier.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_cli.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_code_notes_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_context_scorer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_contract_pipeline.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_coverage_parser.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_cross_consistency.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_dependency_analyzer_node_python.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_dependency_analyzer_polyglot.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_dependency_schema.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_dotnet.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_go_rust_java.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_nodejs.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_php_ruby_dart.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_python.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_universal_managed.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detector_universal_systems.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_detectors_base.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_doc_analyzer_jsdom.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_doc_analyzer_python.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_graph_analyzer_polyglot.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_graph_analyzer_python_node.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_graph_schema.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_hybrid_inference.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_dependencies.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_detection.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_docs.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_graph_modules.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_lqn.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_metrics.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_multistack.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_semantics.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_integration_universal.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_java_spring_integration.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_metrics_analyzer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_packaging.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_phase1_improvements.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_pipeline_integrity.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_real_projects.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_redactor.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_scanner.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_schema.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_schema_normalization.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_semantic_analyzer_node.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_semantic_analyzer_python.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_semantic_import_resolution.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_semantic_schema.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_signal_hierarchy.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_summarizer.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_surface_honesty.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_task_differentiation.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_telemetry.py +0 -0
- {sourcecode-1.27.0 → sourcecode-1.29.0}/tests/test_workspace_analyzer.py +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Continue Here — atlas-cli --agent token reduction
|
|
2
|
+
|
|
3
|
+
**Paused:** 2026-05-16 (sesión 4)
|
|
4
|
+
**Repo:** `/Users/user/Downloads/atlas-cli`
|
|
5
|
+
**Branch:** master
|
|
6
|
+
**Commit:** `f1cb001 corrigiendo bug --agent que duplicaba tokens sin justificación` (committed)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Estado actual
|
|
11
|
+
|
|
12
|
+
Sesión 4: refactorización de `agent_view()` en `serializer.py` completada. **Todo committed.** Working tree limpio.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Qué se hizo esta sesión
|
|
17
|
+
|
|
18
|
+
### Problema resuelto
|
|
19
|
+
|
|
20
|
+
`--agent` producía ~11.337 tokens en SAS vs ~5.885 de `--compact`, con contenido casi idéntico. Objetivo: reducir tokens significativamente manteniendo solo lo que diferencia `--agent`.
|
|
21
|
+
|
|
22
|
+
### Cambios en `agent_view()` (`src/sourcecode/serializer.py`)
|
|
23
|
+
|
|
24
|
+
| Cambio | Impacto |
|
|
25
|
+
|--------|---------|
|
|
26
|
+
| Elimina `production_dependencies` / `dev_tools` / `test_utilities` / `build_tooling` (dep_groups asdict completo) | Mayor fuente de bloat: -3.000-5.000 tokens en repos grandes |
|
|
27
|
+
| `key_dependencies`: de asdict+50 items → `name+version+role+risk_flags` solo, cap 20 | -500-1.500 tokens |
|
|
28
|
+
| Elimina `confidence_summary.hard_signals` / `soft_signals` / `ignored_signals` | -500-1.000 tokens |
|
|
29
|
+
| Elimina bloque `agent_mode` (contenido estático sin señal) | -80 tokens siempre |
|
|
30
|
+
| Elimina `signals.env_vars.keys[]` (duplica compact `env_map`) | -300-500 tokens |
|
|
31
|
+
| `signals.code_notes.top` cap 10→5, strip a `kind+path+line+text` | -200 tokens |
|
|
32
|
+
| Entry points (no-bootstrap): strip a `path+kind+confidence` (como compact) | reducción moderada |
|
|
33
|
+
| Elimina `development_entry_points` (noise para agentes de producción) | pequeña |
|
|
34
|
+
| `architecture.layers` capado a 5 | pequeña |
|
|
35
|
+
|
|
36
|
+
**Reducción estimada para SAS:** −40–60% tokens vs `--agent` anterior.
|
|
37
|
+
|
|
38
|
+
### Campos que permanecen únicos de `--agent`
|
|
39
|
+
|
|
40
|
+
| Campo | Por qué justifica tokens extra vs `--compact` |
|
|
41
|
+
|-------|----------------------------------------------|
|
|
42
|
+
| `file_relevance` (top-20 scored) | Compact no tiene equivalente. Agente sabe qué leer sin explorar. |
|
|
43
|
+
| `architecture.layers` | Compact solo tiene `architecture_summary` string. |
|
|
44
|
+
| `suspicious_dependencies` | Deps declarados sin import observado. Compact no lo tiene. |
|
|
45
|
+
| `confidence_reasons` | Explicación accionable de secciones low-confidence. Compact no lo tiene. |
|
|
46
|
+
|
|
47
|
+
### Tests actualizados
|
|
48
|
+
|
|
49
|
+
- `tests/test_block5_quality.py` — 5 tests de `agent_mode` reemplazados por tests de las nuevas garantías (no `agent_mode`, no `hard_signals`, no `env_vars.keys`, no dep_groups)
|
|
50
|
+
- `tests/test_pipeline_integrity.py` — `test_agent_splits_development_and_auxiliary_eps` → verifica que dev/aux EPs NO aparecen
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Tests al pausar
|
|
55
|
+
|
|
56
|
+
Suite completa (sin fallos pre-existentes): **todo verde**
|
|
57
|
+
|
|
58
|
+
Fallos pre-existentes (no relacionados, ya presentes antes):
|
|
59
|
+
- `test_block2_coverage.py::test_java_marked_unsupported` — DocRecord bug en doc_analyzer.py
|
|
60
|
+
- `test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile` — versión typer mismatch
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Pendiente / próximos pasos opcionales
|
|
65
|
+
|
|
66
|
+
1. **Smoke test en saint-server** para medir tokens reales antes/después:
|
|
67
|
+
```bash
|
|
68
|
+
sourcecode saint-server --agent --output /tmp/agent_new.json
|
|
69
|
+
python3 -c "import json,sys; d=json.load(open('/tmp/agent_new.json')); print('agent tokens (est):', len(json.dumps(d))//4)"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
2. **Verificar que `suspicious_dependencies` es útil en repos reales** — en repos Python/JS (no JVM, donde el index de imports no funciona). En JVM siempre vacío por diseño.
|
|
73
|
+
|
|
74
|
+
3. **Ajustar caps según medición real**: si `file_relevance` (20 files) sigue siendo el mayor contribuyente de tokens, considerar reducir a 15. Medir con `--full` vs default.
|
|
75
|
+
|
|
76
|
+
4. **Smoke tests delta pendientes** de sesión anterior — ver sección "Smoke tests sesión 3" en historial de commits.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Para retomar
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cd /Users/user/Downloads/atlas-cli
|
|
84
|
+
git log --oneline -3
|
|
85
|
+
# debe mostrar: f1cb001 corrigiendo bug --agent que duplicaba tokens sin justificación
|
|
86
|
+
|
|
87
|
+
python3 -m pytest tests/ \
|
|
88
|
+
--ignore=tests/test_block2_coverage.py \
|
|
89
|
+
--deselect=tests/test_dependency_analyzer_node_python.py::test_python_requirements_without_lockfile_keeps_declared_versions \
|
|
90
|
+
-q
|
|
91
|
+
# Expected: todo verde
|
|
92
|
+
|
|
93
|
+
# Verificación rápida agent_view
|
|
94
|
+
python3 -c "
|
|
95
|
+
from sourcecode.serializer import agent_view
|
|
96
|
+
from sourcecode.schema import SourceMap, StackInfo, SourceMapMetadata
|
|
97
|
+
sm = SourceMap(stacks=[StackInfo(stack='python', primary=True)], project_type='python', project_summary='test')
|
|
98
|
+
r = agent_view(sm)
|
|
99
|
+
assert 'agent_mode' not in r
|
|
100
|
+
assert 'production_dependencies' not in r
|
|
101
|
+
assert 'hard_signals' not in r.get('confidence_summary', {})
|
|
102
|
+
print('OK — agent_mode/dep_groups/hard_signals all absent')
|
|
103
|
+
"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
*Pausado 2026-05-16 sesión 4 — gsd:pause-work*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.29.0
|
|
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.
|
|
258
|
+
# sourcecode 1.29.0
|
|
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.
|
|
39
|
+
# sourcecode 1.29.0
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
---
|
|
@@ -1633,7 +1633,7 @@ def prepare_context_cmd(
|
|
|
1633
1633
|
refactor Structural issues, improvement opportunities
|
|
1634
1634
|
generate-tests Untested source files, test gap analysis
|
|
1635
1635
|
onboard Full project context for new agents/developers
|
|
1636
|
-
review-pr
|
|
1636
|
+
review-pr PR diff: changed files, security/transactional impact, test gaps (requires git diff or --since)
|
|
1637
1637
|
delta Incremental context: git-changed files only
|
|
1638
1638
|
|
|
1639
1639
|
\b
|
|
@@ -1642,6 +1642,8 @@ def prepare_context_cmd(
|
|
|
1642
1642
|
sourcecode prepare-context explain /path/to/repo
|
|
1643
1643
|
sourcecode prepare-context fix-bug
|
|
1644
1644
|
sourcecode prepare-context delta --since main
|
|
1645
|
+
sourcecode prepare-context review-pr --since origin/main
|
|
1646
|
+
sourcecode prepare-context review-pr . --since main --output review.json
|
|
1645
1647
|
sourcecode prepare-context onboard --llm-prompt
|
|
1646
1648
|
sourcecode prepare-context --task-help
|
|
1647
1649
|
"""
|
|
@@ -1734,6 +1736,14 @@ def prepare_context_cmd(
|
|
|
1734
1736
|
"test_gaps": False, "code_notes_summary": False,
|
|
1735
1737
|
"changed_files": True, "affected_entry_points": True,
|
|
1736
1738
|
},
|
|
1739
|
+
"review-pr": {
|
|
1740
|
+
"project_summary": False, "architecture_summary": False,
|
|
1741
|
+
"relevant_files": True, "key_dependencies": False,
|
|
1742
|
+
"gaps": True, "confidence": False,
|
|
1743
|
+
"suspected_areas": False, "improvement_opportunities": False,
|
|
1744
|
+
"test_gaps": False, "code_notes_summary": False,
|
|
1745
|
+
"changed_files": True, "affected_entry_points": True,
|
|
1746
|
+
},
|
|
1737
1747
|
}
|
|
1738
1748
|
_content_filter = _TASK_CONTENT_MAP.get(task, {})
|
|
1739
1749
|
|
|
@@ -1813,6 +1823,53 @@ def prepare_context_cmd(
|
|
|
1813
1823
|
out["dependency_graph_summary"] = output.dependency_graph_summary
|
|
1814
1824
|
if output.impact_score_per_file:
|
|
1815
1825
|
out["impact_score_per_file"] = output.impact_score_per_file
|
|
1826
|
+
# review-pr specific fields
|
|
1827
|
+
if task == "review-pr":
|
|
1828
|
+
if output.error_code:
|
|
1829
|
+
_err_out: dict[str, Any] = {
|
|
1830
|
+
"task": output.task,
|
|
1831
|
+
"ci_decision": output.ci_decision or "error",
|
|
1832
|
+
"error": output.error_code,
|
|
1833
|
+
"message": output.error_message,
|
|
1834
|
+
}
|
|
1835
|
+
if output.since:
|
|
1836
|
+
_err_out["since"] = output.since
|
|
1837
|
+
if output.error_hints:
|
|
1838
|
+
_err_out["hint"] = output.error_hints
|
|
1839
|
+
_err_json = json.dumps(_err_out, indent=2, ensure_ascii=False)
|
|
1840
|
+
if output_path is not None:
|
|
1841
|
+
output_path.write_text(_err_json, encoding="utf-8")
|
|
1842
|
+
else:
|
|
1843
|
+
import sys as _sys
|
|
1844
|
+
_sys.stdout.buffer.write(_err_json.encode("utf-8"))
|
|
1845
|
+
_sys.stdout.buffer.write(b"\n")
|
|
1846
|
+
_sys.stdout.buffer.flush()
|
|
1847
|
+
raise typer.Exit(code=1)
|
|
1848
|
+
out["review_type"] = "pull_request"
|
|
1849
|
+
if output.ci_decision:
|
|
1850
|
+
out["ci_decision"] = output.ci_decision
|
|
1851
|
+
if output.base_ref:
|
|
1852
|
+
out["base_ref"] = output.base_ref
|
|
1853
|
+
if output.since:
|
|
1854
|
+
out["since"] = output.since
|
|
1855
|
+
if output.affected_modules:
|
|
1856
|
+
out["affected_modules"] = output.affected_modules
|
|
1857
|
+
if output.security_impact:
|
|
1858
|
+
out["security_impact"] = output.security_impact
|
|
1859
|
+
if output.transactional_impact:
|
|
1860
|
+
out["transactional_impact"] = output.transactional_impact
|
|
1861
|
+
if output.configuration_impact:
|
|
1862
|
+
out["configuration_impact"] = output.configuration_impact
|
|
1863
|
+
if output.test_coverage_risk:
|
|
1864
|
+
out["test_coverage_risk"] = output.test_coverage_risk
|
|
1865
|
+
if output.review_hotspots:
|
|
1866
|
+
out["review_hotspots"] = output.review_hotspots
|
|
1867
|
+
if output.suggested_review_order:
|
|
1868
|
+
out["suggested_review_order"] = output.suggested_review_order
|
|
1869
|
+
if output.impact_summary:
|
|
1870
|
+
out["impact_summary"] = output.impact_summary
|
|
1871
|
+
if output.why_these_files:
|
|
1872
|
+
out["reasoning"] = output.why_these_files
|
|
1816
1873
|
if output.limitations:
|
|
1817
1874
|
out["limitations"] = output.limitations
|
|
1818
1875
|
if output.symptom:
|
|
@@ -338,11 +338,19 @@ class TaskOutput:
|
|
|
338
338
|
error_message: Optional[str] = None
|
|
339
339
|
error_hints: list[str] = field(default_factory=list)
|
|
340
340
|
# CI decision state machine — machine-decidable signal
|
|
341
|
-
ci_decision: Optional[str] = None # "no_changes" | "analysis_success" | "git_ref_error"
|
|
341
|
+
ci_decision: Optional[str] = None # "no_changes" | "analysis_success" | "git_ref_error" | "no_git_repo"
|
|
342
342
|
# git baseline resolution metadata
|
|
343
343
|
resolved_since_ref: Optional[str] = None # actual ref/hash used for the diff
|
|
344
344
|
resolution_path: Optional[str] = None # "exact_local_ref"|"remote_tracking_ref"|"symbolic_ref"|"head_minus_1_fallback"|"uncommitted_changes"|"unresolvable"
|
|
345
345
|
diff_validation_status: Optional[str] = None # "valid_non_empty"|"valid_empty"|"invalid_ref"
|
|
346
|
+
# review-pr specific impact sections
|
|
347
|
+
base_ref: Optional[str] = None
|
|
348
|
+
security_impact: dict = field(default_factory=dict)
|
|
349
|
+
transactional_impact: dict = field(default_factory=dict)
|
|
350
|
+
configuration_impact: dict = field(default_factory=dict)
|
|
351
|
+
test_coverage_risk: dict = field(default_factory=dict)
|
|
352
|
+
review_hotspots: list[str] = field(default_factory=list)
|
|
353
|
+
suggested_review_order: list[str] = field(default_factory=list)
|
|
346
354
|
|
|
347
355
|
|
|
348
356
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -659,6 +667,58 @@ class TaskContextBuilder:
|
|
|
659
667
|
elif _delta_raw:
|
|
660
668
|
_delta_files = set(_delta_raw)
|
|
661
669
|
|
|
670
|
+
# ── 5d. review-pr: git-first gate ──────────────────────────────────────
|
|
671
|
+
if task_name == "review-pr":
|
|
672
|
+
if not self._is_git_repo():
|
|
673
|
+
return TaskOutput(
|
|
674
|
+
task="review-pr", goal=spec.goal,
|
|
675
|
+
project_summary=None, architecture_summary=None,
|
|
676
|
+
relevant_files=[], suspected_areas=[],
|
|
677
|
+
improvement_opportunities=[], test_gaps=[],
|
|
678
|
+
key_dependencies=[], code_notes_summary=None,
|
|
679
|
+
limitations=[], confidence="low",
|
|
680
|
+
error_code="no_git_repo",
|
|
681
|
+
error_message="review-pr requires a git repository.",
|
|
682
|
+
ci_decision="no_git_repo",
|
|
683
|
+
)
|
|
684
|
+
_pr_raw = self._get_git_changed_files(since=since)
|
|
685
|
+
if _pr_raw is None:
|
|
686
|
+
_avail_pr, _sug_pr = self._get_available_refs(since or "")
|
|
687
|
+
_pr_hints: list[str] = []
|
|
688
|
+
if _sug_pr:
|
|
689
|
+
_pr_hints.append(f"Did you mean '{_sug_pr}'?")
|
|
690
|
+
if _avail_pr:
|
|
691
|
+
_pr_hints.append(f"Available refs: {', '.join(_avail_pr[:8])}")
|
|
692
|
+
return TaskOutput(
|
|
693
|
+
task="review-pr", goal=spec.goal,
|
|
694
|
+
project_summary=None, architecture_summary=None,
|
|
695
|
+
relevant_files=[], suspected_areas=[],
|
|
696
|
+
improvement_opportunities=[], test_gaps=[],
|
|
697
|
+
key_dependencies=[], code_notes_summary=None,
|
|
698
|
+
limitations=[], confidence="low",
|
|
699
|
+
since=since,
|
|
700
|
+
error_code="git_ref_not_found",
|
|
701
|
+
error_message=f"Base ref '{since}' not found in this repository.",
|
|
702
|
+
error_hints=_pr_hints,
|
|
703
|
+
gaps=[f"Cannot compute PR diff: git ref '{since}' not found."] + _pr_hints,
|
|
704
|
+
ci_decision="git_ref_error",
|
|
705
|
+
)
|
|
706
|
+
if not _pr_raw:
|
|
707
|
+
_no_diff_hint = "review-pr requires changed files or --since <ref>."
|
|
708
|
+
return TaskOutput(
|
|
709
|
+
task="review-pr", goal=spec.goal,
|
|
710
|
+
project_summary=None, architecture_summary=None,
|
|
711
|
+
relevant_files=[], suspected_areas=[],
|
|
712
|
+
improvement_opportunities=[], test_gaps=[],
|
|
713
|
+
key_dependencies=[], code_notes_summary=None,
|
|
714
|
+
limitations=[], confidence="low",
|
|
715
|
+
error_code="no_diff",
|
|
716
|
+
error_message=f"No PR diff detected. {_no_diff_hint}",
|
|
717
|
+
gaps=[f"No PR diff detected. {_no_diff_hint}"],
|
|
718
|
+
ci_decision="no_changes",
|
|
719
|
+
)
|
|
720
|
+
_delta_files = set(_pr_raw)
|
|
721
|
+
|
|
662
722
|
# ── 5c. review-pr suspected_areas (needs git uncommitted_files) ──────
|
|
663
723
|
if task_name == "review-pr" and spec.enable_code_notes:
|
|
664
724
|
pr_areas: dict[str, int] = {}
|
|
@@ -698,7 +758,7 @@ class TaskContextBuilder:
|
|
|
698
758
|
_delta_dep_graph_summary: dict = {}
|
|
699
759
|
_delta_impact_score_per_file: dict = {}
|
|
700
760
|
|
|
701
|
-
if task_name
|
|
761
|
+
if task_name in ("delta", "review-pr"):
|
|
702
762
|
_delta_changed_list: list[str] = sorted(_delta_files) if _delta_files else []
|
|
703
763
|
(
|
|
704
764
|
relevant_files,
|
|
@@ -727,7 +787,88 @@ class TaskContextBuilder:
|
|
|
727
787
|
delta_files=None,
|
|
728
788
|
)
|
|
729
789
|
|
|
730
|
-
# ── 6b.
|
|
790
|
+
# ── 6b. review-pr: derive PR-specific impact sections from delta analysis ──
|
|
791
|
+
_pr_security_impact: dict = {}
|
|
792
|
+
_pr_transactional_impact: dict = {}
|
|
793
|
+
_pr_configuration_impact: dict = {}
|
|
794
|
+
_pr_test_coverage_risk: dict = {}
|
|
795
|
+
_pr_review_hotspots: list[str] = []
|
|
796
|
+
_pr_suggested_review_order: list[str] = []
|
|
797
|
+
_pr_base_ref: Optional[str] = None
|
|
798
|
+
|
|
799
|
+
if task_name == "review-pr":
|
|
800
|
+
_pr_base_ref = since or "HEAD"
|
|
801
|
+
_sys_risk_areas = _delta_system_impact.get("risk_areas", [])
|
|
802
|
+
|
|
803
|
+
_security_files = [
|
|
804
|
+
f for ra in _sys_risk_areas if ra["area"] == "security"
|
|
805
|
+
for f in ra["affected_files"]
|
|
806
|
+
]
|
|
807
|
+
_transaction_files = [
|
|
808
|
+
f for ra in _sys_risk_areas if ra["area"] in ("transactions", "business_logic")
|
|
809
|
+
for f in ra["affected_files"]
|
|
810
|
+
]
|
|
811
|
+
_config_files = [
|
|
812
|
+
f for ra in _sys_risk_areas if ra["area"] in ("api", "config")
|
|
813
|
+
for f in ra["affected_files"]
|
|
814
|
+
if any(kw in f.lower() for kw in ("config", "properties", "yml", "yaml", "xml", "spring"))
|
|
815
|
+
]
|
|
816
|
+
|
|
817
|
+
if _security_files:
|
|
818
|
+
_pr_security_impact = {
|
|
819
|
+
"affected_resources": _security_files,
|
|
820
|
+
"risk_level": "high",
|
|
821
|
+
}
|
|
822
|
+
if _transaction_files:
|
|
823
|
+
_pr_transactional_impact = {
|
|
824
|
+
"affected_transactions": _transaction_files,
|
|
825
|
+
"risk": "possible transaction boundary change",
|
|
826
|
+
}
|
|
827
|
+
if _config_files:
|
|
828
|
+
_pr_configuration_impact = {"changed_configs": _config_files}
|
|
829
|
+
|
|
830
|
+
# Test coverage risk scoped to changed source files only
|
|
831
|
+
_changed_src = [
|
|
832
|
+
f for f in sorted(_delta_files or set())
|
|
833
|
+
if not self._is_test(f) and self._is_source(f)
|
|
834
|
+
]
|
|
835
|
+
_test_stems = {Path(p).stem for p in test_set}
|
|
836
|
+
_untested_changed = [f for f in _changed_src if Path(f).stem not in _test_stems]
|
|
837
|
+
_test_risk_level = (
|
|
838
|
+
"high" if len(_untested_changed) > 3
|
|
839
|
+
else "medium" if _untested_changed
|
|
840
|
+
else "low"
|
|
841
|
+
)
|
|
842
|
+
_pr_test_coverage_risk = {
|
|
843
|
+
"changed_files_without_tests": _untested_changed[:10],
|
|
844
|
+
"risk_level": _test_risk_level,
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
# Review hotspots: top changed files ranked by impact score
|
|
848
|
+
_pr_review_hotspots = sorted(
|
|
849
|
+
_delta_files or set(),
|
|
850
|
+
key=lambda f: _delta_impact_score_per_file.get(f, 0.0),
|
|
851
|
+
reverse=True,
|
|
852
|
+
)[:8]
|
|
853
|
+
|
|
854
|
+
# Suggested review order: security first, then api → service → persistence → config
|
|
855
|
+
_ORDER_TYPES = ["security", "controller", "service", "repository", "mapper",
|
|
856
|
+
"spring_config", "config", "domain_model", "dto"]
|
|
857
|
+
_seen_order: set[str] = set()
|
|
858
|
+
for _otype in _ORDER_TYPES:
|
|
859
|
+
for _ra in _delta_risk_areas:
|
|
860
|
+
for _f in _ra.get("affected_files", []):
|
|
861
|
+
if _f not in _seen_order:
|
|
862
|
+
_cls = self._classify_changed_file(_f)
|
|
863
|
+
if _cls["artifact_type"] == _otype:
|
|
864
|
+
_pr_suggested_review_order.append(_f)
|
|
865
|
+
_seen_order.add(_f)
|
|
866
|
+
for _f in _pr_review_hotspots:
|
|
867
|
+
if _f not in _seen_order:
|
|
868
|
+
_pr_suggested_review_order.append(_f)
|
|
869
|
+
_seen_order.add(_f)
|
|
870
|
+
|
|
871
|
+
# ── 6c. Symptom keyword boost + related notes (fix-bug + --symptom) ──
|
|
731
872
|
symptom_keywords: list[str] = []
|
|
732
873
|
related_notes: list[dict] = []
|
|
733
874
|
symptom_note: Optional[str] = None
|
|
@@ -883,7 +1024,7 @@ class TaskContextBuilder:
|
|
|
883
1024
|
|
|
884
1025
|
conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
|
|
885
1026
|
confidence = conf_summary.overall
|
|
886
|
-
if task_name
|
|
1027
|
+
if task_name in ("delta", "review-pr"):
|
|
887
1028
|
# Use delta-specific gaps; ConfidenceAnalyzer gaps are about full-repo
|
|
888
1029
|
# detection quality and are not meaningful for an incremental diff.
|
|
889
1030
|
gaps = _delta_analysis_gaps
|
|
@@ -895,16 +1036,16 @@ class TaskContextBuilder:
|
|
|
895
1036
|
gaps.append(_mybatis_warning["reason"])
|
|
896
1037
|
|
|
897
1038
|
# ── 9. why_these_files ────────────────────────────────────────────────
|
|
898
|
-
if task_name
|
|
1039
|
+
if task_name in ("delta", "review-pr"):
|
|
899
1040
|
why_these_files = _delta_why
|
|
900
1041
|
else:
|
|
901
1042
|
why_these_files = {rf.path: rf.reason for rf in relevant_files}
|
|
902
1043
|
|
|
903
|
-
# ── 10. Delta: git changed files + entry points
|
|
1044
|
+
# ── 10. Delta / review-pr: git changed files + entry points ──────────
|
|
904
1045
|
changed_files: list[str] = []
|
|
905
1046
|
affected_entry_points: list[str] = []
|
|
906
|
-
if task_name
|
|
907
|
-
changed_files = sorted(_delta_files) if _delta_files else self._get_git_changed_files(since=since)
|
|
1047
|
+
if task_name in ("delta", "review-pr"):
|
|
1048
|
+
changed_files = sorted(_delta_files) if _delta_files else (self._get_git_changed_files(since=since) or [])
|
|
908
1049
|
_ep_set = {ep.path for ep in entry_points}
|
|
909
1050
|
# include framework-detected entry points AND files classified as
|
|
910
1051
|
# entrypoint/controller/security by artifact taxonomy
|
|
@@ -939,16 +1080,24 @@ class TaskContextBuilder:
|
|
|
939
1080
|
impact_summary=_delta_impact_summary,
|
|
940
1081
|
affected_modules=_delta_affected_modules,
|
|
941
1082
|
risk_areas=_delta_risk_areas,
|
|
942
|
-
since=since if task_name
|
|
1083
|
+
since=since if task_name in ("delta", "review-pr") else None,
|
|
943
1084
|
system_impact=_delta_system_impact,
|
|
944
1085
|
change_type=_delta_change_type,
|
|
945
1086
|
dependency_graph_summary=_delta_dep_graph_summary,
|
|
946
1087
|
impact_score_per_file=_delta_impact_score_per_file,
|
|
947
1088
|
ci_decision=(
|
|
948
1089
|
"no_changes" if task_name == "delta" and not changed_files
|
|
949
|
-
else "analysis_success" if task_name
|
|
1090
|
+
else "analysis_success" if task_name in ("delta", "review-pr")
|
|
950
1091
|
else None
|
|
951
1092
|
),
|
|
1093
|
+
# review-pr specific
|
|
1094
|
+
base_ref=_pr_base_ref,
|
|
1095
|
+
security_impact=_pr_security_impact,
|
|
1096
|
+
transactional_impact=_pr_transactional_impact,
|
|
1097
|
+
configuration_impact=_pr_configuration_impact,
|
|
1098
|
+
test_coverage_risk=_pr_test_coverage_risk,
|
|
1099
|
+
review_hotspots=_pr_review_hotspots,
|
|
1100
|
+
suggested_review_order=_pr_suggested_review_order,
|
|
952
1101
|
)
|
|
953
1102
|
|
|
954
1103
|
def render_prompt(self, output: TaskOutput) -> str:
|
|
@@ -1240,6 +1389,19 @@ class TaskContextBuilder:
|
|
|
1240
1389
|
def _is_source(self, path: str) -> bool:
|
|
1241
1390
|
return Path(path).suffix.lower() in _SOURCE_EXTENSIONS
|
|
1242
1391
|
|
|
1392
|
+
def _is_git_repo(self) -> bool:
|
|
1393
|
+
import subprocess
|
|
1394
|
+
try:
|
|
1395
|
+
r = subprocess.run(
|
|
1396
|
+
["git", "rev-parse", "--git-dir"],
|
|
1397
|
+
cwd=str(self.root),
|
|
1398
|
+
capture_output=True, text=True,
|
|
1399
|
+
encoding="utf-8", errors="replace", timeout=5,
|
|
1400
|
+
)
|
|
1401
|
+
return r.returncode == 0
|
|
1402
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
1403
|
+
return False
|
|
1404
|
+
|
|
1243
1405
|
# ── Delta impact analysis ─────────────────────────────────────────────────
|
|
1244
1406
|
|
|
1245
1407
|
@staticmethod
|
|
@@ -2309,10 +2471,11 @@ class TaskContextBuilder:
|
|
|
2309
2471
|
r = subprocess.run(
|
|
2310
2472
|
["git", "branch", "-a", "--format=%(refname:short)"],
|
|
2311
2473
|
cwd=str(self.root),
|
|
2312
|
-
capture_output=True, text=True,
|
|
2474
|
+
capture_output=True, text=True,
|
|
2475
|
+
encoding="utf-8", errors="replace", timeout=5,
|
|
2313
2476
|
)
|
|
2314
2477
|
if r.returncode == 0:
|
|
2315
|
-
all_refs = [b.strip() for b in r.stdout.splitlines() if b.strip()]
|
|
2478
|
+
all_refs = [b.strip() for b in (r.stdout or "").splitlines() if b.strip()]
|
|
2316
2479
|
branches = [b for b in all_refs if "HEAD" not in b][:10]
|
|
2317
2480
|
ref_lower = invalid_ref.lower()
|
|
2318
2481
|
if ref_lower == "master" and any(b.rstrip("/").endswith("main") for b in all_refs):
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Regression tests for C3 — UTF-8 double-encoding of accented characters.
|
|
2
|
+
|
|
3
|
+
Fixture files:
|
|
4
|
+
- tests/fixtures/latin1_sample_iso.java — Latin-1 encoded (bytes 0xe1, 0xf3, etc.)
|
|
5
|
+
- tests/fixtures/latin1_sample.java — UTF-8 encoded (multibyte sequences)
|
|
6
|
+
|
|
7
|
+
Both should decode cleanly to the correct Unicode characters.
|
|
8
|
+
The old code read Latin-1 files with errors='replace', producing double-encoded garbage.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from sourcecode.tree_utils import safe_read_text
|
|
17
|
+
|
|
18
|
+
FIXTURES = Path(__file__).parent / "fixtures"
|
|
19
|
+
LATIN1_FILE = FIXTURES / "latin1_sample_iso.java"
|
|
20
|
+
UTF8_FILE = FIXTURES / "latin1_sample.java"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestSafeReadText:
|
|
24
|
+
"""safe_read_text must decode both UTF-8 and Latin-1 files correctly."""
|
|
25
|
+
|
|
26
|
+
def test_utf8_file_reads_correctly(self):
|
|
27
|
+
content = safe_read_text(UTF8_FILE)
|
|
28
|
+
# These characters must appear verbatim — not as double-encoded sequences
|
|
29
|
+
assert "días" in content, f"Expected 'días', got: {content[:200]!r}"
|
|
30
|
+
assert "ñ" in content or "función" in content or "ñoño" in content
|
|
31
|
+
assert "Ã" not in content, "Double-encoded: U+00C3 found (Ã). Encoding was mangled."
|
|
32
|
+
|
|
33
|
+
def test_latin1_file_reads_correctly(self):
|
|
34
|
+
content = safe_read_text(LATIN1_FILE)
|
|
35
|
+
# Latin-1 0xe1 = 'á' — must decode to the correct character
|
|
36
|
+
assert "á" in content, f"Expected 'á', got: {content!r}"
|
|
37
|
+
assert "ó" in content, f"Expected 'ó', got: {content!r}"
|
|
38
|
+
# No replacement chars
|
|
39
|
+
assert "�" not in content, "Replacement character found — file was not decoded correctly."
|
|
40
|
+
# No double-encoding: 'á' in double-encoded UTF-8-as-Latin-1 would appear as 'á'
|
|
41
|
+
assert "Ã" not in content, "Double-encoded: U+00C3 found (Ã). Latin-1 file was not decoded correctly."
|
|
42
|
+
|
|
43
|
+
def test_nonexistent_file_raises_oserror(self):
|
|
44
|
+
with pytest.raises(OSError):
|
|
45
|
+
safe_read_text(FIXTURES / "does_not_exist.java")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestCodeNotesEncoding:
|
|
49
|
+
"""CodeNotesAnalyzer must extract notes from Latin-1 files without encoding corruption."""
|
|
50
|
+
|
|
51
|
+
def test_latin1_todo_extracted_cleanly(self):
|
|
52
|
+
from sourcecode.code_notes_analyzer import CodeNotesAnalyzer
|
|
53
|
+
|
|
54
|
+
# analyze() scans FIXTURES dir — Latin-1 file is there
|
|
55
|
+
analyzer = CodeNotesAnalyzer()
|
|
56
|
+
notes, _, _ = analyzer.analyze(FIXTURES)
|
|
57
|
+
# Should have extracted TODO without replacement chars
|
|
58
|
+
todo_texts = [n.text for n in notes if "latin1_sample_iso" in n.path]
|
|
59
|
+
for text in todo_texts:
|
|
60
|
+
assert "â" not in text, f"Double-encoded byte found in TODO text: {text!r}"
|
|
61
|
+
assert "Ã" not in text, f"Double-encoded char in TODO text: {text!r}"
|
|
62
|
+
assert "�" not in text, f"Replacement char in TODO text: {text!r}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TestGetAvailableRefsWindowsEncoding:
|
|
66
|
+
"""Regression: _get_available_refs crashed on Windows due to cp1252 encoding and None stdout.
|
|
67
|
+
|
|
68
|
+
Bug: subprocess.run(text=True) without encoding= uses the OS default (cp1252 on Windows).
|
|
69
|
+
Git output containing byte 0x8d raises UnicodeDecodeError (not caught). If stdout is None,
|
|
70
|
+
r.stdout.splitlines() raises AttributeError. Both cause unhandled tracebacks.
|
|
71
|
+
Fix: encoding="utf-8", errors="replace", and (r.stdout or "").splitlines().
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def _make_builder(self) -> "TaskContextBuilder":
|
|
75
|
+
from sourcecode.prepare_context import TaskContextBuilder
|
|
76
|
+
return TaskContextBuilder(FIXTURES)
|
|
77
|
+
|
|
78
|
+
def test_case_a_stdout_none_does_not_crash(self, monkeypatch):
|
|
79
|
+
"""stdout=None must not raise AttributeError on splitlines()."""
|
|
80
|
+
import subprocess
|
|
81
|
+
import types
|
|
82
|
+
|
|
83
|
+
fake = types.SimpleNamespace(returncode=0, stdout=None, stderr="")
|
|
84
|
+
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake)
|
|
85
|
+
|
|
86
|
+
builder = self._make_builder()
|
|
87
|
+
refs, suggested = builder._get_available_refs("main")
|
|
88
|
+
assert refs == []
|
|
89
|
+
assert suggested is None
|
|
90
|
+
|
|
91
|
+
def test_case_b_non_utf8_bytes_no_unicode_error(self, monkeypatch):
|
|
92
|
+
"""Bytes invalid in cp1252 (e.g. 0x8d) must not raise UnicodeDecodeError.
|
|
93
|
+
|
|
94
|
+
With encoding='utf-8' + errors='replace', replacement chars appear instead.
|
|
95
|
+
"""
|
|
96
|
+
import subprocess
|
|
97
|
+
import types
|
|
98
|
+
|
|
99
|
+
# Simulate git returning a branch name that survived errors="replace" decoding
|
|
100
|
+
# (the replacement char � stands in for the bad byte)
|
|
101
|
+
replaced = "develop\nfeature/caf�\n"
|
|
102
|
+
fake = types.SimpleNamespace(returncode=0, stdout=replaced, stderr="")
|
|
103
|
+
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake)
|
|
104
|
+
|
|
105
|
+
builder = self._make_builder()
|
|
106
|
+
refs, _ = builder._get_available_refs("main")
|
|
107
|
+
assert "develop" in refs
|
|
108
|
+
|
|
109
|
+
def test_case_c_nonexistent_ref_returns_available_refs(self, monkeypatch):
|
|
110
|
+
"""Non-existent --since ref must yield a list of available branches, not a crash."""
|
|
111
|
+
import subprocess
|
|
112
|
+
import types
|
|
113
|
+
|
|
114
|
+
fake = types.SimpleNamespace(
|
|
115
|
+
returncode=0,
|
|
116
|
+
stdout="develop\norigin/develop\nrelease/1.2\n",
|
|
117
|
+
stderr="",
|
|
118
|
+
)
|
|
119
|
+
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake)
|
|
120
|
+
|
|
121
|
+
builder = self._make_builder()
|
|
122
|
+
refs, suggested = builder._get_available_refs("main")
|
|
123
|
+
assert "develop" in refs
|
|
124
|
+
# invalid_ref="main", all_refs contains no "master" → no suggestion
|
|
125
|
+
assert suggested is None
|
|
126
|
+
|
|
127
|
+
def test_case_d_nonzero_returncode_returns_empty(self, monkeypatch):
|
|
128
|
+
"""returncode != 0 must not parse stdout — returns empty list silently."""
|
|
129
|
+
import subprocess
|
|
130
|
+
import types
|
|
131
|
+
|
|
132
|
+
fake = types.SimpleNamespace(returncode=128, stdout="fatal: not a git repo\n", stderr="")
|
|
133
|
+
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake)
|
|
134
|
+
|
|
135
|
+
builder = self._make_builder()
|
|
136
|
+
refs, suggested = builder._get_available_refs("main")
|
|
137
|
+
assert refs == []
|
|
138
|
+
assert suggested is None
|