sourcecode 4.12.0__tar.gz → 4.14.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-4.14.0/.github/workflows/perf-gate.yml +109 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/CHANGELOG.md +127 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/PKG-INFO +3 -3
- {sourcecode-4.12.0 → sourcecode-4.14.0}/README.md +2 -2
- {sourcecode-4.12.0 → sourcecode-4.14.0}/pyproject.toml +1 -1
- sourcecode-4.14.0/scripts/perf_gate.py +176 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architectural_baseline.py +5 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cache_model.py +53 -1
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cli.py +203 -4
- sourcecode-4.14.0/src/sourcecode/parallel.py +241 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/parse_cache.py +183 -1
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/perf.py +268 -4
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/progress.py +99 -46
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/readiness_timeline.py +2 -0
- sourcecode-4.14.0/src/sourcecode/readonly.py +171 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/release_info.py +1 -1
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/repository_ir.py +24 -1
- sourcecode-4.14.0/src/sourcecode/rule_pass.py +236 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/runs.py +3 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/security_config_scan.py +36 -5
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_findings.py +82 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_security_audit.py +103 -13
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_tx_analyzer.py +82 -14
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/verify_edit.py +3 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/verify_repo.py +3 -0
- sourcecode-4.12.0/src/sourcecode/rule_pass.py +0 -112
- {sourcecode-4.12.0 → sourcecode-4.14.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/.gitignore +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/.ruff.toml +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/CLAUDE.md +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/CONTRIBUTING.md +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/LICENSE +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/SECURITY.md +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/raw +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/scripts/perf_harness.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/archetype.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architectural_delta.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/audit_report.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/baseline_autocapture.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/caller_reach.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/chain_rules.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/change_plan.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/client_calls.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/compare.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/constraint_diff.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/container_wiring.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_diff.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_init.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/data_exposure.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/data_labels.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/defect_identity.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/degradation.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/deployment_prefix.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detach.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/envelope.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/environment_resolution.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/execution_plan.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/facts/__init__.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/facts/registry.json +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/filter_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/git_checkout.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/identity_fallback.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/integration_coordinates.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/license.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/dependency_import.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/migration_blast.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/non_coverage.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/openrewrite_recipe.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/output_encoding.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/path_admission.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/phased_run.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/pipe_contract.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/posture.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/provenance.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/reference_facts.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/remedies.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/risk.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/rule_catalog.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/sarif.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/servlet_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/source_text.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_impact.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_profiles.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_properties.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/target_admission.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/test_gap_ranking.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/test_sources.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/text_input.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/token_estimate.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/verify_rules.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/README.md +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-4.12.0 → sourcecode-4.14.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
name: Performance regression gate
|
|
2
|
+
|
|
3
|
+
# F-AP. Field evaluation #16 measured seven commands 2-3.4x slower on a minor
|
|
4
|
+
# release while every payload stayed byte-identical, and named the cause:
|
|
5
|
+
# "no es que haya regresiones - es que no hay nada que las detecte".
|
|
6
|
+
# This is the detector. The threshold, the repository and the gated command
|
|
7
|
+
# population are declared in `sourcecode.perf` and documented in
|
|
8
|
+
# `docs/perf/REGRESSION-GATE.md`; nothing here carries a number of its own.
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
schedule:
|
|
12
|
+
# Nightly, after the day's merges have settled.
|
|
13
|
+
- cron: "0 3 * * *"
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
inputs:
|
|
16
|
+
baseline_ref:
|
|
17
|
+
description: "Baseline directory under docs/perf/baselines (default: gate-latest)"
|
|
18
|
+
required: false
|
|
19
|
+
default: "gate-latest"
|
|
20
|
+
|
|
21
|
+
concurrency:
|
|
22
|
+
group: perf-gate
|
|
23
|
+
cancel-in-progress: false
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
gate:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
timeout-minutes: 120
|
|
29
|
+
env:
|
|
30
|
+
# Declares the host class so nightly runs on ephemeral runners are
|
|
31
|
+
# comparable at all. Recorded in every artifact as `env.host_id_declared`,
|
|
32
|
+
# so the declaration is visible rather than silent.
|
|
33
|
+
SOURCECODE_PERF_HOST_ID: github-ubuntu-latest
|
|
34
|
+
BASELINE_DIR: docs/perf/baselines/${{ github.event.inputs.baseline_ref || 'gate-latest' }}
|
|
35
|
+
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout
|
|
38
|
+
uses: actions/checkout@v4
|
|
39
|
+
|
|
40
|
+
- name: Setup Python
|
|
41
|
+
uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: "3.11"
|
|
44
|
+
|
|
45
|
+
- name: Install
|
|
46
|
+
run: |
|
|
47
|
+
python -m pip install --upgrade pip
|
|
48
|
+
pip install .
|
|
49
|
+
|
|
50
|
+
- name: Prove the gate is armed
|
|
51
|
+
# A green gate is evidence only when a red one is reachable in the same
|
|
52
|
+
# job with the same constants. This step fails if a synthetic 3x-slower
|
|
53
|
+
# cell would have passed.
|
|
54
|
+
run: python3 scripts/perf_gate.py --self-test
|
|
55
|
+
|
|
56
|
+
- name: Read the declared gate configuration
|
|
57
|
+
id: cfg
|
|
58
|
+
run: |
|
|
59
|
+
python3 scripts/perf_gate.py --print-config | tee gate-config.json
|
|
60
|
+
echo "repo_url=$(python3 -c "import json;print(json.load(open('gate-config.json'))['repository_url'])")" >> "$GITHUB_OUTPUT"
|
|
61
|
+
echo "repo=$(python3 -c "import json;print(json.load(open('gate-config.json'))['repository'])")" >> "$GITHUB_OUTPUT"
|
|
62
|
+
echo "mode=$(python3 -c "import json;print(json.load(open('gate-config.json'))['mode'])")" >> "$GITHUB_OUTPUT"
|
|
63
|
+
echo "runs=$(python3 -c "import json;print(json.load(open('gate-config.json'))['min_runs'])")" >> "$GITHUB_OUTPUT"
|
|
64
|
+
echo "commands=$(python3 -c "import json;print(' '.join(repr(c) for c in json.load(open('gate-config.json'))['commands']))")" >> "$GITHUB_OUTPUT"
|
|
65
|
+
|
|
66
|
+
- name: Clone the gate repository
|
|
67
|
+
run: |
|
|
68
|
+
mkdir -p fleet
|
|
69
|
+
git clone --depth 1 "${{ steps.cfg.outputs.repo_url }}" "fleet/${{ steps.cfg.outputs.repo }}"
|
|
70
|
+
|
|
71
|
+
- name: Measure
|
|
72
|
+
env:
|
|
73
|
+
PERF_FLEET_ROOT: fleet
|
|
74
|
+
run: |
|
|
75
|
+
python3 scripts/perf_harness.py \
|
|
76
|
+
--repos "${{ steps.cfg.outputs.repo }}" \
|
|
77
|
+
--commands ${{ steps.cfg.outputs.commands }} \
|
|
78
|
+
--modes "${{ steps.cfg.outputs.mode }}" \
|
|
79
|
+
--runs "${{ steps.cfg.outputs.runs }}" \
|
|
80
|
+
--fleet-root fleet \
|
|
81
|
+
--out perf-run
|
|
82
|
+
|
|
83
|
+
- name: Gate
|
|
84
|
+
run: |
|
|
85
|
+
python3 scripts/perf_gate.py \
|
|
86
|
+
--baseline "$BASELINE_DIR" \
|
|
87
|
+
--current perf-run \
|
|
88
|
+
--report perf-run/gate.json \
|
|
89
|
+
--table perf-run/RELEASE-TABLE.md
|
|
90
|
+
|
|
91
|
+
- name: Publish the measured table
|
|
92
|
+
if: always()
|
|
93
|
+
run: |
|
|
94
|
+
if [ -f perf-run/RELEASE-TABLE.md ]; then
|
|
95
|
+
{
|
|
96
|
+
echo "## Measured wall time per command"
|
|
97
|
+
echo
|
|
98
|
+
cat perf-run/RELEASE-TABLE.md
|
|
99
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
- name: Upload artifacts
|
|
103
|
+
if: always()
|
|
104
|
+
uses: actions/upload-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
name: perf-gate-${{ github.run_number }}
|
|
107
|
+
path: |
|
|
108
|
+
perf-run/**
|
|
109
|
+
gate-config.json
|
|
@@ -2,6 +2,133 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [4.14.0] — 2026-08-09
|
|
6
|
+
|
|
7
|
+
**The Part 1 queue of Amendment N — the four rows field evaluation #17 opened,
|
|
8
|
+
one commit each.** The fourth measurement of the same repository at the same
|
|
9
|
+
commit scored **7,5/10**, the highest of the series, and verified six of the
|
|
10
|
+
previous release's fixes from the outside. Both rows it opened are in the fields
|
|
11
|
+
that describe ASK's *own* reliability, which is where this product can least
|
|
12
|
+
afford to be wrong.
|
|
13
|
+
|
|
14
|
+
**Fixed in this release:**
|
|
15
|
+
|
|
16
|
+
- **A truncated answer no longer raises its own confidence (C2-31).** Three
|
|
17
|
+
complete runs published 395 findings / 98 defects at `confidence_level:
|
|
18
|
+
medium`; the run the budget cut published 289 / 15 at **`high`**, because the
|
|
19
|
+
level is derived from the confidence of the high/critical findings *that
|
|
20
|
+
exist* and the family contributing the medium-confidence ones is the family
|
|
21
|
+
that did not run. `SpringAuditResult.partial` is now one flag — set by the
|
|
22
|
+
scopes, by the CLI for a phase that never started, and carried through
|
|
23
|
+
`merge` — and a partial answer is capped at `low` with `confidence_basis`
|
|
24
|
+
saying the cut is the reason. `summary` gains `partial`, `counts_are_floor`
|
|
25
|
+
and `counts_basis`, so the mark lives where the numbers a consumer reads are,
|
|
26
|
+
not only in `_partial` at the root. The cap is a ceiling and never a floor: a
|
|
27
|
+
complete run keeps its level and gains no fields.
|
|
28
|
+
- **The rule inventory names rules, and both scopes' (C1-42).** `rules_run`
|
|
29
|
+
listed four SEC ids in a payload whose findings carried DEAD-001, SEC-008,
|
|
30
|
+
TX-006, TX-001, SEC-005 and SEC-006, and `rules_not_run` said `[GATE-001]`
|
|
31
|
+
where the arithmetic proves GATE-004 did not run either. The lists were keyed
|
|
32
|
+
on the pattern *object's* identity, so a family emitting six rules stood for
|
|
33
|
+
itself, and `merge` overwrote the TX scope's inventory with the security
|
|
34
|
+
scope's. `rule_pass.rule_ids_of()` is the one authority, the two lists are
|
|
35
|
+
unioned across scopes, and `rules_run_count` / `rules_total` sit beside them.
|
|
36
|
+
Three invariants are asserted over a real audit: every rule that fired is in
|
|
37
|
+
`rules_run`, the lists never overlap, and a complete `--scope all` run
|
|
38
|
+
inventories exactly `rule_catalog.ids()`.
|
|
39
|
+
- **The heartbeat is emitted by the work, not only by a thread (C3-80).** Four
|
|
40
|
+
versions with the same shape: one line at `elapsed=20.1s`, 39 minutes of
|
|
41
|
+
nothing, then the same stage text seconds before the end. A line-mode
|
|
42
|
+
heartbeat printed only by a daemon thread depends on that thread being
|
|
43
|
+
scheduled during exactly the CPU-bound stretch the operator is asking about.
|
|
44
|
+
`Progress._emit()` is now one seam with one throttle and two callers — the
|
|
45
|
+
loop on its timer and the analysis in-band — sharing one `_last_emit` stamp,
|
|
46
|
+
so the line rate is unchanged and a running analysis is always heard from.
|
|
47
|
+
- **The budget cuts inside a rule family (C3-79).** Budget 420 s, elapsed
|
|
48
|
+
2 376 s: `exhausted()` was asked on a whole-family boundary and one family
|
|
49
|
+
lasts 39 minutes. `RulePassProgress.tick()` reports counted work and answers
|
|
50
|
+
*"is the time spent"* in one call, sampling the clock once per 64 units; five
|
|
51
|
+
families that walk a population take a `FamilyBudget` and stop on a batch
|
|
52
|
+
boundary. A family cut in flight is in neither list — it is
|
|
53
|
+
`rules_partially_run` with the count it reached, and the findings it produced
|
|
54
|
+
are kept, because they were measured. Measured on BroadleafCommerce: a 4,5 s
|
|
55
|
+
budget stops at 1 408 of 2 766 Java files and ends at 5,1 s.
|
|
56
|
+
|
|
57
|
+
**Verified in the field, not changed here:** `verify` exiting 2 without contracts
|
|
58
|
+
is C3-75's deliberate half (the waiver `--allow-unverified` and the named
|
|
59
|
+
`unverified_reason` are the fix; a distinct code is a contract change, F-AV), and
|
|
60
|
+
the 4.10.7 performance that never came back is C3-76, which does not reproduce on
|
|
61
|
+
this platform and waits on the Windows runner (F-AW).
|
|
62
|
+
|
|
63
|
+
Suite 7 787.
|
|
64
|
+
|
|
65
|
+
## [4.13.0] — 2026-08-08
|
|
66
|
+
|
|
67
|
+
**The capability queue of Amendment M — the four features field evaluation #16
|
|
68
|
+
asked for, one commit each, after Part 1 closed in 4.12.0.** The evaluation's
|
|
69
|
+
verdict on engineering was *"no es que haya regresiones — es que no hay nada que
|
|
70
|
+
las detecte"*, and its "if you could only do two things" was F-AP and F-AQ, in
|
|
71
|
+
that order. Both ship here, with F-AR and F-AS beside them.
|
|
72
|
+
|
|
73
|
+
**New in this release:**
|
|
74
|
+
|
|
75
|
+
- **A release performance gate (F-AP).** `perf.gate_report()` turns a whole run
|
|
76
|
+
into one verdict against a whole baseline, and three of its four outcomes are
|
|
77
|
+
not a pass: a cell over the threshold **fails**, a baselined cell this run did
|
|
78
|
+
not measure **fails** (silence is how "nothing detects a regression" happens),
|
|
79
|
+
and a cell under the ADR-0006 sample floor **fails**. `scripts/perf_gate.py
|
|
80
|
+
--self-test` proves the gate is armed by failing a synthetic 3×-slower cell —
|
|
81
|
+
CI runs it *before* it runs the measurement, because a green gate is evidence
|
|
82
|
+
only when a red one is reachable on the same constants. Threshold **20 %**,
|
|
83
|
+
warm mode, one ~3 000-file repository, six repository-wide commands, and the
|
|
84
|
+
three exclusions published with every verdict. All of it lives in
|
|
85
|
+
`sourcecode.perf`; `docs/perf/REGRESSION-GATE.md` embeds `--print-config`'s
|
|
86
|
+
output verbatim and a test asserts they are the same bytes.
|
|
87
|
+
- **The measured times are published, generated (F-AP, second rider).**
|
|
88
|
+
`perf.release_table()` builds the per-command / per-repository-scale table a
|
|
89
|
+
release note carries, from the run's own artifacts. An axis that was not
|
|
90
|
+
measured prints `unknown`, never `0`. And `cache model`'s figures now name the
|
|
91
|
+
build they were measured on (3.2.2) beside the build you are running, so a
|
|
92
|
+
table nine releases old cannot read as this build's measurement.
|
|
93
|
+
- **`--jobs N` / `-j` / `ASK_JOBS` (F-AQ)**, default `cpu_count() - 1`. The
|
|
94
|
+
fourth consecutive evaluation to ask for parallelism was the first to sample
|
|
95
|
+
it: `threads=1`, `CPU/wall = 0,92…0,95`, 54 minutes, 20 cores. Worker
|
|
96
|
+
processes now parse files into the *same* content-addressed cache the IR loop
|
|
97
|
+
is about to read, so the loop is untouched and **byte-identity is structural,
|
|
98
|
+
not inspected** — no worker output reaches the payload by any route. Measured
|
|
99
|
+
on 2 766 Java files, both sides parse-cold with their own cache root: 5,36 s →
|
|
100
|
+
3,46 s (**1,55×**), CPU/wall 2,07, IR hash identical. Every failure mode — no
|
|
101
|
+
pool, a faulting worker, a frozen build, a disabled cache, a repository under
|
|
102
|
+
the 200-file floor — degrades to today's sequential parse and returns a receipt
|
|
103
|
+
naming the reason.
|
|
104
|
+
- **`ASK_PARSE_CACHE_MAX_MB` (F-AR)**, default **512 MB**, least-recently-used,
|
|
105
|
+
enforced on write and published in `cache status` beside the size it bounds.
|
|
106
|
+
C3-70 retires the generation that can never be read again — an exact rule, and
|
|
107
|
+
exact rules only cover what they can foresee. This bounds what they cannot:
|
|
108
|
+
many repositories, many branches, one machine, and an owner who never runs
|
|
109
|
+
`ask cache clear`. Reads refresh recency (hourly granularity, so a hit is not a
|
|
110
|
+
disk write), an entry and its body-fact blob are evicted together, and
|
|
111
|
+
`ASK_PARSE_CACHE_MAX_MB=0` turns the guard off deliberately and says so.
|
|
112
|
+
- **`--no-write` / `ASK_READONLY=1` (F-AS)** — nothing is created inside the
|
|
113
|
+
analysed repository, and a command that cannot answer without writing says so
|
|
114
|
+
instead of writing, in the same error envelope every other refusal uses.
|
|
115
|
+
Accepted by **every** command (consumed before the parser, the `--detach`
|
|
116
|
+
precedent), because a mode five commands accept and seventy-nine reject is a
|
|
117
|
+
mode whose one refusing run is the run that writes. Each refusal is announced
|
|
118
|
+
once on stderr with its path. Shared caches outside the repository are
|
|
119
|
+
untouched — an auditor is protecting a checkout, not a home directory.
|
|
120
|
+
- **`ask --help` names every command that writes into the repository**, with the
|
|
121
|
+
condition beside it ("only with `--snapshot`"), generated from one authority.
|
|
122
|
+
|
|
123
|
+
**Measured, not fixed, and said so:** the second half of F-AQ — *"one audit per
|
|
124
|
+
run, shared by the compositions"* — was measured before being touched.
|
|
125
|
+
Instrumenting `build_risk` on a real repository counts `run_tx_audit` **1×** and
|
|
126
|
+
`run_security_audit` **1×**: `risk` already hands its findings to every impact
|
|
127
|
+
chain it composes (C3-42-b), so the duplication the field named is between two
|
|
128
|
+
*processes*, not inside one. That needs a persisted audit keyed on the worktree
|
|
129
|
+
signature, and it is not in this release. What ships is the regression test that
|
|
130
|
+
keeps the in-run property true.
|
|
131
|
+
|
|
5
132
|
## [4.12.0] — 2026-08-08
|
|
6
133
|
|
|
7
134
|
**Field evaluation #16 — the third round on the same repository and commit, and the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.14.0
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Keywords: agents,ai,codebase,context,developer-tools,llm
|
|
@@ -42,7 +42,7 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
|
|
43
43
|
**Context · Impact · Migration · Architecture · Review — everything from one structural model.**
|
|
44
44
|
|
|
45
|
-

|
|
46
46
|

|
|
47
47
|
|
|
48
48
|
> **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
|
|
@@ -108,7 +108,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
108
108
|
# pip / pipx
|
|
109
109
|
pipx install sourcecode # or: pip install sourcecode
|
|
110
110
|
|
|
111
|
-
ask version # ask 4.
|
|
111
|
+
ask version # ask 4.14.0 — and, on a build that has aged,
|
|
112
112
|
# how many releases have probably shipped since
|
|
113
113
|
```
|
|
114
114
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Context · Impact · Migration · Architecture · Review — everything from one structural model.**
|
|
6
6
|
|
|
7
|
-

|
|
8
8
|

|
|
9
9
|
|
|
10
10
|
> **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
|
|
@@ -70,7 +70,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
70
70
|
# pip / pipx
|
|
71
71
|
pipx install sourcecode # or: pip install sourcecode
|
|
72
72
|
|
|
73
|
-
ask version # ask 4.
|
|
73
|
+
ask version # ask 4.14.0 — and, on a build that has aged,
|
|
74
74
|
# how many releases have probably shipped since
|
|
75
75
|
```
|
|
76
76
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Release performance gate (F-AP) — compare a run against a baseline, one verdict.
|
|
3
|
+
|
|
4
|
+
`scripts/perf_harness.py` measures; this decides. It reads two directories of
|
|
5
|
+
`perf-baseline-v1` cells, gates every cell it can match, and exits non-zero when
|
|
6
|
+
any of them regressed beyond the declared threshold, when a baselined cell was not
|
|
7
|
+
measured at all, or when a cell's sample is under the ADR-0006 floor.
|
|
8
|
+
|
|
9
|
+
The threshold, the repository, the mode and the excluded commands are constants in
|
|
10
|
+
`sourcecode.perf` — this script never carries a number of its own, and
|
|
11
|
+
`docs/perf/REGRESSION-GATE.md` is generated from the same constants.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
# gate a fresh run against the last release's baseline
|
|
15
|
+
python3 scripts/perf_gate.py \
|
|
16
|
+
--baseline docs/perf/baselines/4.12.0 \
|
|
17
|
+
--current docs/perf/baselines/4.13.0 \
|
|
18
|
+
--report gate.json --table RELEASE-TABLE.md
|
|
19
|
+
|
|
20
|
+
# prove the gate is armed: a synthetic 3x-slower cell must fail it
|
|
21
|
+
python3 scripts/perf_gate.py --self-test
|
|
22
|
+
|
|
23
|
+
# print the declared configuration (what the doc renders from)
|
|
24
|
+
python3 scripts/perf_gate.py --print-config
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import copy
|
|
31
|
+
import json
|
|
32
|
+
import sys
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
|
|
36
|
+
from sourcecode import perf # noqa: E402
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_cells(directory: Path) -> dict[str, dict]:
|
|
40
|
+
"""Every `perf-baseline-v1` cell in *directory*, keyed by `<repo>__<cmd>__<mode>`.
|
|
41
|
+
|
|
42
|
+
Gate artifacts (`*.gate.json`) and anything that is not a cell are skipped
|
|
43
|
+
rather than mis-read: a directory that also holds a report must not turn the
|
|
44
|
+
report into a measurement.
|
|
45
|
+
"""
|
|
46
|
+
cells: dict[str, dict] = {}
|
|
47
|
+
if not directory.is_dir():
|
|
48
|
+
return cells
|
|
49
|
+
for path in sorted(directory.glob("*.json")):
|
|
50
|
+
if path.name.endswith(".gate.json"):
|
|
51
|
+
continue
|
|
52
|
+
try:
|
|
53
|
+
cell = json.loads(path.read_text(encoding="utf-8"))
|
|
54
|
+
except (OSError, ValueError):
|
|
55
|
+
continue
|
|
56
|
+
if not isinstance(cell, dict) or cell.get("schema_version") != perf.SCHEMA_VERSION:
|
|
57
|
+
continue
|
|
58
|
+
cells[perf.cell_id(cell)] = cell
|
|
59
|
+
return cells
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _self_test() -> int:
|
|
63
|
+
"""A deliberately slowed command must fail the gate. Run in CI before the gate.
|
|
64
|
+
|
|
65
|
+
This is the exit criterion stated as an executable check: it is not enough for
|
|
66
|
+
the gate to exist and be green — a green gate is only evidence when a red one
|
|
67
|
+
is reachable on the same machine, in the same job, with the same constants.
|
|
68
|
+
"""
|
|
69
|
+
base = perf.build_baseline_cell(
|
|
70
|
+
tool_version="self-test",
|
|
71
|
+
command="spring-audit",
|
|
72
|
+
repo={"name": perf.RELEASE_GATE_REPO, "commit": "self-test", "files": 3000},
|
|
73
|
+
mode=perf.RELEASE_GATE_MODE,
|
|
74
|
+
wall_ms_runs=[1000.0] * perf.RELEASE_GATE_MIN_RUNS,
|
|
75
|
+
rss_bytes_runs=[1.0] * perf.RELEASE_GATE_MIN_RUNS,
|
|
76
|
+
)
|
|
77
|
+
slowed = copy.deepcopy(base)
|
|
78
|
+
slowed["wall_ms"] = {k: v * 3.0 for k, v in base["wall_ms"].items()}
|
|
79
|
+
|
|
80
|
+
cid = perf.cell_id(base)
|
|
81
|
+
red = perf.gate_report({cid: base}, {cid: slowed})
|
|
82
|
+
green = perf.gate_report({cid: base}, {cid: base})
|
|
83
|
+
dropped = perf.gate_report({cid: base}, {})
|
|
84
|
+
|
|
85
|
+
ok = (not red["passed"]) and green["passed"] and (not dropped["passed"])
|
|
86
|
+
print(f"self-test: 3x slower -> {'FAIL (correct)' if not red['passed'] else 'PASS (BROKEN)'}")
|
|
87
|
+
print(f"self-test: unchanged -> {'PASS (correct)' if green['passed'] else 'FAIL (BROKEN)'}")
|
|
88
|
+
print(f"self-test: cell gone -> {'FAIL (correct)' if not dropped['passed'] else 'PASS (BROKEN)'}")
|
|
89
|
+
if not ok:
|
|
90
|
+
print("perf gate self-test FAILED: the gate would not catch a regression.",
|
|
91
|
+
file=sys.stderr)
|
|
92
|
+
return 0 if ok else 1
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _print_config() -> int:
|
|
96
|
+
print(json.dumps({
|
|
97
|
+
"schema_version": perf.GATE_SCHEMA_VERSION,
|
|
98
|
+
"threshold": perf.RELEASE_GATE_THRESHOLD,
|
|
99
|
+
"repository": perf.RELEASE_GATE_REPO,
|
|
100
|
+
"repository_url": perf.RELEASE_GATE_REPO_URL,
|
|
101
|
+
"repository_scale": perf.RELEASE_GATE_REPO_SCALE,
|
|
102
|
+
"mode": perf.RELEASE_GATE_MODE,
|
|
103
|
+
"min_runs": perf.RELEASE_GATE_MIN_RUNS,
|
|
104
|
+
"commands": list(perf.RELEASE_GATE_COMMANDS),
|
|
105
|
+
"exclusions": [
|
|
106
|
+
{"command": c, "reason": r} for c, r in perf.RELEASE_GATE_EXCLUSIONS
|
|
107
|
+
],
|
|
108
|
+
}, indent=2))
|
|
109
|
+
return 0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def main() -> int:
|
|
113
|
+
ap = argparse.ArgumentParser(
|
|
114
|
+
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
|
|
115
|
+
)
|
|
116
|
+
ap.add_argument("--baseline", type=Path, help="Directory of the release being compared against.")
|
|
117
|
+
ap.add_argument("--current", type=Path, help="Directory of this run's cells.")
|
|
118
|
+
ap.add_argument("--threshold", type=float, default=perf.RELEASE_GATE_THRESHOLD,
|
|
119
|
+
help=f"Max allowed regression ratio (declared default: "
|
|
120
|
+
f"{perf.RELEASE_GATE_THRESHOLD}).")
|
|
121
|
+
ap.add_argument("--min-runs", type=int, default=perf.RELEASE_GATE_MIN_RUNS)
|
|
122
|
+
ap.add_argument("--allow-incomparable", action="store_true",
|
|
123
|
+
help="Do not fail cells whose env/host differ. Off by default: "
|
|
124
|
+
"an incomparable cell is an unmeasured one.")
|
|
125
|
+
ap.add_argument("--report", type=Path, help="Write the perf-gate-v1 report here.")
|
|
126
|
+
ap.add_argument("--table", type=Path, help="Write the release-notes markdown table here.")
|
|
127
|
+
ap.add_argument("--self-test", action="store_true",
|
|
128
|
+
help="Prove the gate is armed; measures nothing.")
|
|
129
|
+
ap.add_argument("--print-config", action="store_true",
|
|
130
|
+
help="Print the declared gate configuration as JSON.")
|
|
131
|
+
args = ap.parse_args()
|
|
132
|
+
|
|
133
|
+
if args.self_test:
|
|
134
|
+
return _self_test()
|
|
135
|
+
if args.print_config:
|
|
136
|
+
return _print_config()
|
|
137
|
+
if not args.current:
|
|
138
|
+
ap.error("--current is required (or use --self-test / --print-config)")
|
|
139
|
+
|
|
140
|
+
current = load_cells(args.current)
|
|
141
|
+
if not current:
|
|
142
|
+
print(f"no perf cells in {args.current} — nothing was measured, "
|
|
143
|
+
f"which the gate reads as a failure, not a pass.", file=sys.stderr)
|
|
144
|
+
return 1
|
|
145
|
+
baseline = load_cells(args.baseline) if args.baseline else {}
|
|
146
|
+
|
|
147
|
+
report = perf.gate_report(
|
|
148
|
+
baseline, current,
|
|
149
|
+
max_regression=args.threshold,
|
|
150
|
+
allow_incomparable=args.allow_incomparable,
|
|
151
|
+
require_min_runs=args.min_runs,
|
|
152
|
+
)
|
|
153
|
+
table = perf.release_table(list(current.values()), gate=report)
|
|
154
|
+
|
|
155
|
+
if args.report:
|
|
156
|
+
args.report.parent.mkdir(parents=True, exist_ok=True)
|
|
157
|
+
args.report.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
|
|
158
|
+
if args.table:
|
|
159
|
+
args.table.parent.mkdir(parents=True, exist_ok=True)
|
|
160
|
+
args.table.write_text(table, encoding="utf-8")
|
|
161
|
+
|
|
162
|
+
print(table)
|
|
163
|
+
if not baseline:
|
|
164
|
+
print("no baseline directory — cells recorded, nothing compared.", file=sys.stderr)
|
|
165
|
+
for cid in report["regressions"]:
|
|
166
|
+
print(f"REGRESSION: {cid}", file=sys.stderr)
|
|
167
|
+
for cid in report["missing_cells"]:
|
|
168
|
+
print(f"NOT MEASURED (baselined): {cid}", file=sys.stderr)
|
|
169
|
+
for cid in report["under_sampled_cells"]:
|
|
170
|
+
print(f"UNDER-SAMPLED: {cid}", file=sys.stderr)
|
|
171
|
+
|
|
172
|
+
return 0 if report["passed"] else 1
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
raise SystemExit(main())
|
|
@@ -37,6 +37,7 @@ from pathlib import Path
|
|
|
37
37
|
from typing import TYPE_CHECKING, Any
|
|
38
38
|
|
|
39
39
|
from sourcecode import __version__ as TOOL_VERSION
|
|
40
|
+
from sourcecode import readonly as _readonly
|
|
40
41
|
from sourcecode.architectural_delta import ArchMetrics, diff_metrics, extract_metrics
|
|
41
42
|
from sourcecode.perf import collect_env
|
|
42
43
|
|
|
@@ -412,6 +413,9 @@ def protect_ask_dir(out_dir: Path, *, announce: bool = True) -> None:
|
|
|
412
413
|
marker = ask_dir / ".gitignore"
|
|
413
414
|
if not marker.exists():
|
|
414
415
|
created = not ask_dir.exists()
|
|
416
|
+
# F-AS: the guard raises an OSError, which the `except OSError` below
|
|
417
|
+
# turns into "did not write" — the mode's promise, no new branch.
|
|
418
|
+
_readonly.guard(ask_dir, what="this tool's own directory")
|
|
415
419
|
ask_dir.mkdir(parents=True, exist_ok=True)
|
|
416
420
|
marker.write_text(_ASK_DIR_GITIGNORE, encoding="utf-8")
|
|
417
421
|
if announce and created:
|
|
@@ -442,6 +446,7 @@ def _announce_ask_dir(ask_dir: Path) -> None:
|
|
|
442
446
|
|
|
443
447
|
def write_baseline(baseline: dict, out_dir: Path) -> Path:
|
|
444
448
|
"""Write `baseline` as deterministic JSON under `out_dir`; return the file path."""
|
|
449
|
+
_readonly.guard(out_dir, what="baseline history directory")
|
|
445
450
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
446
451
|
protect_ask_dir(out_dir)
|
|
447
452
|
path = out_dir / _baseline_filename(baseline)
|
|
@@ -99,6 +99,15 @@ REFERENCE_REPOSITORY = (
|
|
|
99
99
|
#: figures are relative to.
|
|
100
100
|
REFERENCE_JAVA_FILES = 2000
|
|
101
101
|
|
|
102
|
+
#: The build the reference figures were measured on. F-AP's second rider: C3-60
|
|
103
|
+
#: conditioned these figures on the repository in hand; this is the other half —
|
|
104
|
+
#: **keeping them current**, or, when they are not current, saying so. A table
|
|
105
|
+
#: measured nine releases ago and printed without its provenance is a prediction
|
|
106
|
+
#: presented as a measurement, which is the one thing this product does not do.
|
|
107
|
+
#: The nightly gate (`docs/perf/REGRESSION-GATE.md`) is what re-measures them; this
|
|
108
|
+
#: constant is what makes the gap visible until it does.
|
|
109
|
+
REFERENCE_MEASURED_VERSION = "3.2.2"
|
|
110
|
+
|
|
102
111
|
#: The other end of the measured range, and the reason this module publishes a
|
|
103
112
|
#: *class* rather than a projected duration (C4-19). Field evaluation #16, on
|
|
104
113
|
#: Windows 11 / PowerShell 5.1 / pipx / Python 3.10: `spring-audit` on a
|
|
@@ -164,7 +173,9 @@ LAYERS: tuple[Layer, ...] = (
|
|
|
164
173
|
location="~/.sourcecode/parse-cache-v1/",
|
|
165
174
|
invalidated_by=(
|
|
166
175
|
"the file's own bytes and the extractor's source — content-addressed, "
|
|
167
|
-
"so it is never stale and never needs invalidating"
|
|
176
|
+
"so it is never stale and never needs invalidating. Bounded by "
|
|
177
|
+
"`ASK_PARSE_CACHE_MAX_MB` (default 512 MB, least-recently-used, "
|
|
178
|
+
"enforced on write) and reported in `cache status` (F-AR)"
|
|
168
179
|
),
|
|
169
180
|
warmed="filled for every Java file the warm parses",
|
|
170
181
|
),
|
|
@@ -358,6 +369,34 @@ class Conditioning:
|
|
|
358
369
|
)
|
|
359
370
|
|
|
360
371
|
|
|
372
|
+
def reference_currency() -> dict:
|
|
373
|
+
"""How old the reference figures are, in releases the reader can name (F-AP).
|
|
374
|
+
|
|
375
|
+
Returns the build the figures were measured on, the build running now, and
|
|
376
|
+
whether they are the same. The gap is not converted into a count of releases:
|
|
377
|
+
versions are not a distance and this module does not invent one. Naming both
|
|
378
|
+
builds is enough for a reader to see that a figure was measured on a different
|
|
379
|
+
one — which is the whole defect: *"no predice nada a 3 342 ficheros"* applies
|
|
380
|
+
to the size axis, and this is the same failure on the time axis.
|
|
381
|
+
"""
|
|
382
|
+
from sourcecode import __version__
|
|
383
|
+
|
|
384
|
+
current = __version__
|
|
385
|
+
return {
|
|
386
|
+
"measured_on_version": REFERENCE_MEASURED_VERSION,
|
|
387
|
+
"running_version": current,
|
|
388
|
+
"current": REFERENCE_MEASURED_VERSION == current,
|
|
389
|
+
"statement": (
|
|
390
|
+
f"Measured on {REFERENCE_MEASURED_VERSION}; you are running {current}. "
|
|
391
|
+
f"The figures are that build's on that repository — they are not a "
|
|
392
|
+
f"prediction for this one, and the nightly performance gate "
|
|
393
|
+
f"(docs/perf/REGRESSION-GATE.md) is what re-measures them."
|
|
394
|
+
if REFERENCE_MEASURED_VERSION != current
|
|
395
|
+
else f"Measured on {current}, the build you are running."
|
|
396
|
+
),
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
|
|
361
400
|
def as_dict(here: "Optional[Conditioning]" = None) -> dict:
|
|
362
401
|
"""The whole model as data — what `ask cache model --json` emits."""
|
|
363
402
|
conditioned: dict = {}
|
|
@@ -386,6 +425,9 @@ def as_dict(here: "Optional[Conditioning]" = None) -> dict:
|
|
|
386
425
|
],
|
|
387
426
|
"reference_repository": REFERENCE_REPOSITORY,
|
|
388
427
|
"reference_java_files": REFERENCE_JAVA_FILES,
|
|
428
|
+
# F-AP rider: the figures carry the build they were measured on, so a
|
|
429
|
+
# table nine releases old cannot read as this build's measurement.
|
|
430
|
+
"reference_currency": reference_currency(),
|
|
389
431
|
# Published beside the reference so no consumer reads the figures as a
|
|
390
432
|
# law: the same command has been measured 46× slower on a repository 1.7×
|
|
391
433
|
# the size, on a different platform (C4-19).
|
|
@@ -427,6 +469,15 @@ def render_markdown() -> str:
|
|
|
427
469
|
out.append("")
|
|
428
470
|
out.append(f"Measured on {REFERENCE_REPOSITORY}, each command in isolation.")
|
|
429
471
|
out.append("")
|
|
472
|
+
# The build, not the gap: this block is a checked-in document, so naming the
|
|
473
|
+
# *running* version here would make the doc drift on every release. `cache
|
|
474
|
+
# model` names both, because there a reader has a running build to compare to.
|
|
475
|
+
out.append(
|
|
476
|
+
f"Measured on release {REFERENCE_MEASURED_VERSION}. Figures are that "
|
|
477
|
+
f"build's on that repository, not a prediction for yours; the nightly "
|
|
478
|
+
f"performance gate (`docs/perf/REGRESSION-GATE.md`) is what re-measures them."
|
|
479
|
+
)
|
|
480
|
+
out.append("")
|
|
430
481
|
out.append("| Command | A warm gives it | Measured (nothing cached → after a warm) | Repeat run cached | Layers | Notes |")
|
|
431
482
|
out.append("|---|---|---|---|---|---|")
|
|
432
483
|
for cmd in COMMANDS:
|
|
@@ -504,6 +555,7 @@ def render_text(here: "Optional[Conditioning]" = None) -> str:
|
|
|
504
555
|
lines.append("")
|
|
505
556
|
lines.append("What a warm gives each command")
|
|
506
557
|
lines.append(f" Timings: {REFERENCE_REPOSITORY}, each command measured in isolation.")
|
|
558
|
+
lines.append(f" {reference_currency()['statement']}")
|
|
507
559
|
width = max(len(c.command) for c in COMMANDS)
|
|
508
560
|
for cmd in COMMANDS:
|
|
509
561
|
repeat = "repeat cached" if cmd.repeat else "recomputes"
|