sourcecode 4.11.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.11.0 → sourcecode-4.14.0}/CHANGELOG.md +189 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/PKG-INFO +3 -3
- {sourcecode-4.11.0 → sourcecode-4.14.0}/README.md +2 -2
- {sourcecode-4.11.0 → sourcecode-4.14.0}/pyproject.toml +1 -1
- sourcecode-4.14.0/scripts/perf_gate.py +176 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/architectural_baseline.py +50 -2
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/cache_model.py +83 -16
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/cli.py +425 -28
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/container_wiring.py +21 -0
- sourcecode-4.14.0/src/sourcecode/mcp/dependency_import.py +87 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/registry.py +6 -1
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/server.py +20 -2
- sourcecode-4.14.0/src/sourcecode/parallel.py +241 -0
- sourcecode-4.14.0/src/sourcecode/parse_cache.py +521 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/perf.py +268 -4
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/phased_run.py +61 -10
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/progress.py +116 -46
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/readiness_timeline.py +12 -1
- sourcecode-4.14.0/src/sourcecode/readonly.py +171 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/release_info.py +1 -1
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/repository_ir.py +44 -3
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_impact.py +5 -2
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/risk.py +120 -38
- sourcecode-4.14.0/src/sourcecode/rule_pass.py +236 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/runs.py +3 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/security_config_scan.py +36 -5
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_findings.py +82 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_impact.py +29 -3
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_security_audit.py +146 -9
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_tx_analyzer.py +110 -10
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/verify_edit.py +3 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/verify_repo.py +62 -9
- sourcecode-4.11.0/src/sourcecode/cache.tmp_new +0 -772
- sourcecode-4.11.0/src/sourcecode/parse_cache.py +0 -180
- {sourcecode-4.11.0 → sourcecode-4.14.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/.gitignore +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/.ruff.toml +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/CLAUDE.md +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/CONTRIBUTING.md +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/LICENSE +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/SECURITY.md +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/raw +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/scripts/perf_harness.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/archetype.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/architectural_delta.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/audit_report.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/baseline_autocapture.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/caller_reach.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/canonical_ir.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/chain_rules.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/change_plan.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/client_calls.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/compare.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/constraint_diff.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/contract_diff.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/contract_init.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/data_exposure.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/data_labels.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/defect_identity.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/degradation.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/deployment_prefix.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detach.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/envelope.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/environment_resolution.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/execution_plan.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/facts/__init__.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/facts/registry.json +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/filter_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/git_checkout.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/identity_fallback.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/integration_coordinates.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/license.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/migration_blast.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/non_coverage.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/openrewrite_recipe.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/output_encoding.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/path_admission.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/pipe_contract.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/posture.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/provenance.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/reference_facts.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/remedies.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/rule_catalog.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/sarif.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/servlet_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/source_text.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_profiles.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_properties.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/target_admission.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/test_gap_ranking.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/test_sources.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/text_input.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/token_estimate.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/verify_rules.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/supabase/functions/README.md +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-4.11.0 → sourcecode-4.14.0}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-4.11.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,195 @@
|
|
|
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
|
+
|
|
132
|
+
## [4.12.0] — 2026-08-08
|
|
133
|
+
|
|
134
|
+
**Field evaluation #16 — the third round on the same repository and commit, and the
|
|
135
|
+
first A/B/C of three of our own releases (4.10.6 → 4.10.7 → 4.11.0, 7,0 → 7,0 →
|
|
136
|
+
6,5).** The three value axes did not move: correctness 9,5, unique value 9,0,
|
|
137
|
+
epistemic honesty 10, with `endpoints.json`, `servlets.json` and `validation.json`
|
|
138
|
+
**byte-identical across all three builds**. What fell is engineering, and the
|
|
139
|
+
sentence is about us: *"no es que haya regresiones — es que no hay nada que las
|
|
140
|
+
detecte."* Nine rows close here, one commit each.
|
|
141
|
+
|
|
142
|
+
**Behaviour changes to read before upgrading a pipeline:**
|
|
143
|
+
|
|
144
|
+
- **`risk_score` and `risk_score_raw` are `null`, not `0.0`, when the container
|
|
145
|
+
invokes the queried component** (`@Configuration`, `@Aspect`, a registered
|
|
146
|
+
filter). The band already said `high` for these while the number said `0.0` — the
|
|
147
|
+
worst possible value for the most critical symbol in a system, from the same
|
|
148
|
+
object. `risk_score_basis` says why and names the surfaces that do measure such a
|
|
149
|
+
component. Affects `impact-chain`, `impact`, `plan` and `compare`; a consumer
|
|
150
|
+
reading the field with a `0.0` default now sees `null`.
|
|
151
|
+
- **`ASK_MAX_ANALYSIS_SECONDS` actually stops a `spring-audit` rule pass.** It was
|
|
152
|
+
only ever checked between the three phases, and the last one holds ~85 % of the
|
|
153
|
+
time, so a 420 s budget produced a 2 351 s run and no `partial` anywhere. A cut
|
|
154
|
+
run now publishes `metadata.rules_not_run`, a `PARTIAL:` limitation, and
|
|
155
|
+
`_partial.phases_stopped_inside`. `metadata.rules_run` is present on every run.
|
|
156
|
+
- **`migrate-check` no longer creates `<scope>/.ask/` unless you pass `--snapshot`.**
|
|
157
|
+
A read-only run left a git-ignored directory in the audited repository — invisible
|
|
158
|
+
to `git status`, visible only to `git status --ignored`.
|
|
159
|
+
- **`verify` gains `--allow-unverified`** (exit 0 while the verdict stays
|
|
160
|
+
`unverified`) and publishes `unverified_reason`: `no_contracts_declared` or
|
|
161
|
+
`gate_could_not_run`. Exit codes are unchanged; the remedy is now the first reason.
|
|
162
|
+
- **The parse cache retires generations.** Entries live under the producing
|
|
163
|
+
analyzer's fingerprint, and a run purges the generations that can never be read
|
|
164
|
+
again — the field measured +142,53 MB per release, three copies of one repository,
|
|
165
|
+
427,59 MB after three runs. `cache status` publishes the composition.
|
|
166
|
+
|
|
167
|
+
**Also in this release:**
|
|
168
|
+
|
|
169
|
+
- The rule pass reports the family it is on, counted against the total, and names
|
|
170
|
+
the two stages that have no population (`spring-audit` and `risk`). The ETA is
|
|
171
|
+
withheld there on purpose: rule families are not comparable units.
|
|
172
|
+
- `risk` checkpoints — after the audit and every 25 composed rows — with the partial
|
|
173
|
+
file built by the same assembler as the answer, so it is the same document over
|
|
174
|
+
fewer rows.
|
|
175
|
+
- The budget message stops quoting a class constant in the grammar of a
|
|
176
|
+
measurement. It carries this command's measured anchors and the size of the
|
|
177
|
+
repository in hand, and says plainly that no projection is offered. Anchors
|
|
178
|
+
recalibrated: `spring-audit` 408 s → 2 351 s at 3 342 files, `endpoints` 4,2 → 5,0,
|
|
179
|
+
`migrate-check --compact` 26,7 and `posture` 38,0 (both previously recorded as not
|
|
180
|
+
finishing), `risk` 3 232.
|
|
181
|
+
- The MCP stack's incomplete-model warning no longer reaches stderr, and the models
|
|
182
|
+
it complains about are rebuilt rather than only muted.
|
|
183
|
+
- A 772-line temp file (`cache.tmp_new`) was shipping inside the wheel; removed, and
|
|
184
|
+
the package contents are now asserted by allow-list.
|
|
185
|
+
|
|
186
|
+
**Measured and not fixed, deliberately:** the block performance regression the
|
|
187
|
+
evaluation reports (seven commands 2–3,4× slower on a minor) **does not reproduce
|
|
188
|
+
here** — the A/B of 4.10.7 against 4.11.0 on a 2 985-file repository lands within
|
|
189
|
+
1 % on six commands, with `posture --resolve-environments` byte-identical, and
|
|
190
|
+
`risk` could not be A/B'd because the *4.10.7* side alone passed 61 minutes. Its
|
|
191
|
+
cost is structural, not new. The numbers are in the ledger; the row waits on a
|
|
192
|
+
Windows runner rather than on a guess.
|
|
193
|
+
|
|
5
194
|
## [4.11.0] — 2026-08-07
|
|
6
195
|
|
|
7
196
|
**The queue behind Tier D: five ledger rows, and three batteries that were red in
|
|
@@ -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())
|
|
@@ -30,12 +30,14 @@ the perf baselines do.
|
|
|
30
30
|
from __future__ import annotations
|
|
31
31
|
|
|
32
32
|
import json
|
|
33
|
+
import os
|
|
33
34
|
import subprocess
|
|
34
35
|
from datetime import datetime, timezone
|
|
35
36
|
from pathlib import Path
|
|
36
37
|
from typing import TYPE_CHECKING, Any
|
|
37
38
|
|
|
38
39
|
from sourcecode import __version__ as TOOL_VERSION
|
|
40
|
+
from sourcecode import readonly as _readonly
|
|
39
41
|
from sourcecode.architectural_delta import ArchMetrics, diff_metrics, extract_metrics
|
|
40
42
|
from sourcecode.perf import collect_env
|
|
41
43
|
|
|
@@ -365,21 +367,40 @@ def _baseline_filename(baseline: dict) -> str:
|
|
|
365
367
|
#: a 14 587-entry structural inventory of the code. A self-confined
|
|
366
368
|
#: `.ask/.gitignore` is the honest form: it never touches the user's own
|
|
367
369
|
#: `.gitignore`, and it stops applying the moment they delete the directory.
|
|
370
|
+
#: C3-74 corrected the last line. It used to read *"Delete this file to version
|
|
371
|
+
#: them deliberately"*, and deleting it is the one action that would commit a
|
|
372
|
+
#: baseline carrying `env.hostname_hash`, `cpu` and `cores` — an invitation to do
|
|
373
|
+
#: the thing the file exists to prevent. It now states the consequence instead.
|
|
368
374
|
_ASK_DIR_GITIGNORE = (
|
|
369
375
|
"# Written by `ask` the first time it created this directory.\n"
|
|
370
376
|
"# These are local analysis artefacts: a structural inventory of your code\n"
|
|
371
377
|
"# and, if you asked for it, the machine that measured it. Nothing here\n"
|
|
372
|
-
"# belongs in a commit
|
|
378
|
+
"# belongs in a commit.\n"
|
|
379
|
+
"#\n"
|
|
380
|
+
"# Removing this file makes everything here committable, including any\n"
|
|
381
|
+
"# recorded machine fingerprint (`--record-env`: hostname hash, CPU, cores).\n"
|
|
382
|
+
"# Version these deliberately only if you intend to publish that.\n"
|
|
373
383
|
"*\n"
|
|
374
384
|
)
|
|
375
385
|
|
|
376
386
|
|
|
377
|
-
def protect_ask_dir(out_dir: Path) -> None:
|
|
387
|
+
def protect_ask_dir(out_dir: Path, *, announce: bool = True) -> None:
|
|
378
388
|
"""Give a freshly created `.ask/` its own `.gitignore` (C3-68).
|
|
379
389
|
|
|
380
390
|
Best-effort and never overwriting: a user who edited it decided something,
|
|
381
391
|
and a tool that re-imposes its default on every run is worse than one that
|
|
382
392
|
never wrote it.
|
|
393
|
+
|
|
394
|
+
**Call this where the write happens, never where the path is computed.** This
|
|
395
|
+
function *creates* the directory in order to protect it, so calling it
|
|
396
|
+
speculatively is a write — which is how `migrate-check` came to leave a
|
|
397
|
+
`.ask/.gitignore` inside a repository it had only read (C3-74).
|
|
398
|
+
|
|
399
|
+
`announce` puts one line on stderr the first time the directory is created,
|
|
400
|
+
with its path. A side effect inside somebody else's repository is exactly what
|
|
401
|
+
I-8 says must be declared, and this one hides from the check an auditor makes:
|
|
402
|
+
`git status --porcelain` is clean because the ignore file ignores itself, and
|
|
403
|
+
only `--ignored` reveals `!! <scope>/.ask/`.
|
|
383
404
|
"""
|
|
384
405
|
try:
|
|
385
406
|
ask_dir = Path(out_dir)
|
|
@@ -391,14 +412,41 @@ def protect_ask_dir(out_dir: Path) -> None:
|
|
|
391
412
|
return
|
|
392
413
|
marker = ask_dir / ".gitignore"
|
|
393
414
|
if not marker.exists():
|
|
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")
|
|
394
419
|
ask_dir.mkdir(parents=True, exist_ok=True)
|
|
395
420
|
marker.write_text(_ASK_DIR_GITIGNORE, encoding="utf-8")
|
|
421
|
+
if announce and created:
|
|
422
|
+
_announce_ask_dir(ask_dir)
|
|
396
423
|
except OSError:
|
|
397
424
|
return
|
|
398
425
|
|
|
399
426
|
|
|
427
|
+
def _announce_ask_dir(ask_dir: Path) -> None:
|
|
428
|
+
"""One line, on stderr, naming what was created and how to move it (C3-74)."""
|
|
429
|
+
import sys
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
# No flag is named here on purpose: the commands that write under `.ask/`
|
|
433
|
+
# spell the override differently (`baseline capture --dir`,
|
|
434
|
+
# `migrate-check --history-dir`), and a message that names the wrong one
|
|
435
|
+
# is the class of defect this ledger is about.
|
|
436
|
+
sys.stderr.write(
|
|
437
|
+
f"[ask] created {ask_dir}{os.sep} for this run's local artefacts — "
|
|
438
|
+
f"git-ignored by its own .gitignore, so `git status` will not show it "
|
|
439
|
+
f"(`git status --ignored` will). The command's directory option takes a "
|
|
440
|
+
f"path outside the repository if you want them elsewhere.\n"
|
|
441
|
+
)
|
|
442
|
+
sys.stderr.flush()
|
|
443
|
+
except Exception:
|
|
444
|
+
pass
|
|
445
|
+
|
|
446
|
+
|
|
400
447
|
def write_baseline(baseline: dict, out_dir: Path) -> Path:
|
|
401
448
|
"""Write `baseline` as deterministic JSON under `out_dir`; return the file path."""
|
|
449
|
+
_readonly.guard(out_dir, what="baseline history directory")
|
|
402
450
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
403
451
|
protect_ask_dir(out_dir)
|
|
404
452
|
path = out_dir / _baseline_filename(baseline)
|