sourcecode 4.1.0__tar.gz → 4.2.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.1.0 → sourcecode-4.2.0}/CHANGELOG.md +153 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/PKG-INFO +15 -1
- {sourcecode-4.1.0 → sourcecode-4.2.0}/README.md +14 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/pyproject.toml +1 -1
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/__init__.py +1 -1
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/caller_reach.py +16 -7
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/canonical_ir.py +29 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/cli.py +235 -3
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/contract_diff.py +5 -4
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/environment_resolution.py +100 -6
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/facts/registry.json +15 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/git_checkout.py +5 -2
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/non_coverage.py +25 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/posture.py +393 -7
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/remedies.py +18 -1
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/risk.py +73 -15
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_impact.py +14 -3
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/verify_repo.py +5 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/.gitignore +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/.ruff.toml +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/CONTRIBUTING.md +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/LICENSE +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/SECURITY.md +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/raw +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/scripts/perf_harness.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/archetype.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/architectural_baseline.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/architectural_delta.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/cache.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/cache_model.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/chain_rules.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/change_plan.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/classifier.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/compare.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/constraint_diff.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/contract_init.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/defect_identity.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/degradation.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/deployment_prefix.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/envelope.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/explain.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/facts/__init__.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/filter_surface.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/hibernate_strat.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/identity_fallback.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/integration_coordinates.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/license.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/migration_blast.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/parse_cache.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/path_admission.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/perf.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/pipe_contract.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/pr_impact.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/progress.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/readiness_timeline.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/redactor.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/reference_facts.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/release_info.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/repository_ir.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/ris.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/rule_catalog.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/scanner.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/schema.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/security_config.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/security_config_scan.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/serializer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_profiles.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_properties.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/target_admission.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/test_gap_ranking.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/test_sources.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/token_estimate.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/verify_edit.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/verify_rules.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/version_check.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/src/sourcecode/workspace.py +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/functions/README.md +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-4.1.0 → sourcecode-4.2.0}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -2,6 +2,159 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [4.2.0] — 2026-08-03
|
|
6
|
+
|
|
7
|
+
Minor, not a patch, by this project's own criterion: two published figures move
|
|
8
|
+
and a verdict changes, so a pipeline reading either can change colour.
|
|
9
|
+
`posture --resolve-environments` answers `NOT DECIDED IN THIS REPOSITORY` where
|
|
10
|
+
it answered `artefacts_disagree` on a repository whose build properties nothing
|
|
11
|
+
consumes (C1-22), and `ask risk` publishes defects where it published findings,
|
|
12
|
+
so `total_defects` and `by_band` move (C1-23) — 20 → 15 on BroadleafCommerce.
|
|
13
|
+
`risk` also drops `rule_id` for `rule_ids`: it is in the `experimental` tier,
|
|
14
|
+
whose published promise is exactly that its shape may change in a minor.
|
|
15
|
+
|
|
16
|
+
Field evaluation #9 (8/10, same 3 337-file monolith as #6–#8) opened nine rows
|
|
17
|
+
and this release closes seven of them. The two left open are open on purpose:
|
|
18
|
+
`C3-39` needs a Windows runner before it needs a fix, and `P-7` is a pricing
|
|
19
|
+
decision. Every complaint was re-measured against HEAD before a row was written
|
|
20
|
+
— five reproduced, one was half false, one was not reproducible here, and one
|
|
21
|
+
was a capability that already shipped.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **`ask posture --diff-ref <base>:<head>` and `--since <ref>` — one profile set,
|
|
26
|
+
two git refs** (roadmap M10.5 item 1). `--diff` holds the tree fixed and moves
|
|
27
|
+
the profile set; this holds the profile set fixed and moves the tree, which is
|
|
28
|
+
the question a pull request asks: *does this change what a client can reach
|
|
29
|
+
without authenticating?* `--since` compares a ref with the working tree,
|
|
30
|
+
uncommitted edits included. Both refs are read with `git archive`: nothing is
|
|
31
|
+
written to the repository, no worktree is created and the index is not touched.
|
|
32
|
+
`docs/posture.md` had declared this as the one requested extension with no
|
|
33
|
+
feature behind it.
|
|
34
|
+
- **`--fail-on opened|any|never` — the gate half.** Default `never`, so no
|
|
35
|
+
invocation that worked yesterday changes colour today; `opened` exits 1 when a
|
|
36
|
+
request a readable rule leaves reachable unauthenticated was not one before.
|
|
37
|
+
The payload is emitted before the exit code, because a red pipeline with no
|
|
38
|
+
statement of what opened is a gate nobody keeps. It requires a ref comparison —
|
|
39
|
+
gating a single run would gate on pre-existing debt, which is `spring-audit --ci`.
|
|
40
|
+
- **`endpoint_request_identity` registered as a fact** (ADR-0008 R11,
|
|
41
|
+
`sourcecode.canonical_ir:request_identity`). The comparison unit is the request
|
|
42
|
+
(`METHOD path`), not the endpoint id, which carries the controller FQN and the
|
|
43
|
+
handler symbol and therefore changes when a handler moves between classes
|
|
44
|
+
without the URL changing. `contract_diff` re-derived the same identity and now
|
|
45
|
+
binds to the authority. Path-variable patterns are deliberately **not**
|
|
46
|
+
normalised: `{id:.*}` and `{id:[^0-9].*}` are different URLs to a caller, and
|
|
47
|
+
one field repository moved 93 requests with exactly that edit in a single commit.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- **`ask endpoints --compact` — the census instead of the rows** (C2-22, field
|
|
52
|
+
evaluation #9). Asked how exposed a repository was, the command answered with
|
|
53
|
+
1.9 MB of JSON for 3 574 endpoints and no summary mode, and the evaluator
|
|
54
|
+
aggregated it by hand. Every answer now carries an `exposure` block — counts by
|
|
55
|
+
method, by security policy, by controller and by first path area — and
|
|
56
|
+
`--compact` returns it without the rows, declaring what it dropped. Two rules
|
|
57
|
+
it obeys against itself: the census is measured over the population the filters
|
|
58
|
+
chose and never over what `--limit` left rendered, and every ranked rollup
|
|
59
|
+
states its own total when it cuts. Measured on BroadleafCommerce: 95 KB → 11 KB,
|
|
60
|
+
every count unchanged. (The other half of the complaint is recorded as false:
|
|
61
|
+
`spring-audit` has shipped `--compact`, `--min-severity` and `--scope` for
|
|
62
|
+
releases.)
|
|
63
|
+
|
|
64
|
+
- **NC-007 — the endpoint population is declared** (CL-12, field evaluation #9).
|
|
65
|
+
`endpoints` projects Spring handler mappings; a servlet declared in `web.xml`
|
|
66
|
+
or registered as a `ServletRegistrationBean` is reachable over HTTP and is in
|
|
67
|
+
no part of that model. Six published non-coverage rows are exactly what let a
|
|
68
|
+
reader treat the list as the whole HTTP surface, and the evaluation lost a
|
|
69
|
+
monitoring console that way — at a path behind the credential the same run
|
|
70
|
+
reported as SEC-007. The row is published in the `endpoints` payload and inside
|
|
71
|
+
`posture.endpoints`, because the reader who counts open requests is reading
|
|
72
|
+
`posture`. Admission of a servlet surface is deliberately **not** in this
|
|
73
|
+
change: it is a second population and merging it into `endpoints.total` would
|
|
74
|
+
move a published count and every ratio keyed on it.
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- **A reach figure that did not name its scope** (C2-23, field evaluation #9).
|
|
79
|
+
`risk` reported `endpoints_reached: 1` for a symbol whose class `impact-chain`
|
|
80
|
+
said reached 8 — both correct, both documented, in two payloads, so reconciling
|
|
81
|
+
them cost the reader three unit notes. Each row now carries
|
|
82
|
+
`endpoints_reached_basis`: what the figure is a reach *of* (a defect sits at the
|
|
83
|
+
symbol that carries the remedy, so a method-level defect walks from the method)
|
|
84
|
+
and, where that is narrower than a class, the command that answers the wider
|
|
85
|
+
question. The relationship is asserted by measurement rather than stated in
|
|
86
|
+
prose.
|
|
87
|
+
|
|
88
|
+
- **The remedy registry now follows the status, not only the limitation** (C4-13,
|
|
89
|
+
field evaluation #9 — the sixth appearance of the C4 shape). The evaluation met
|
|
90
|
+
`verify`'s `contract_count: 0` → `unverified` → exit 2 under a read-only
|
|
91
|
+
constraint, concluded the command was unusable and asked for a `--dry-run` that
|
|
92
|
+
shipped in 3.7.0. The state named one next move and the reader could not take
|
|
93
|
+
it. `Remedy` gained `also` (the further options an answer needs — a remedy is an
|
|
94
|
+
invocation, not a flag), the new entry is `ask verify . --init --dry-run`, and
|
|
95
|
+
the `unverified` reason names both forms. The guard grew with it: every option
|
|
96
|
+
of an invocation must be accepted by the command it names, and the README check
|
|
97
|
+
matches a line carrying the command with all of its options, so a front page
|
|
98
|
+
that lists `--init` and omits `--dry-run` fails the build.
|
|
99
|
+
|
|
100
|
+
- **The hub-class guard contradicted the payload beside it** (C2-21, field
|
|
101
|
+
evaluation #9). It printed *"symbol has > 500 direct callers"* while the same
|
|
102
|
+
run published 215 of them on openmrs-core's `Context`: the guard trips on
|
|
103
|
+
caller **reference sites** (method-level keys included) and the payload
|
|
104
|
+
publishes caller **classes**, so the stated reason was checkably false while
|
|
105
|
+
the decision was right. `caller_reach.bfs_callers` now returns the population it
|
|
106
|
+
read, and the warning names it with its unit next to the class count it takes
|
|
107
|
+
from `caller_metrics.caller_classes` — *"792 caller reference sites … exceed the
|
|
108
|
+
cap of 500 … The 215 direct caller classes this run publishes stay exact"*.
|
|
109
|
+
|
|
110
|
+
- **`ask risk` ranked findings under the label `spring-audit` deduplicates**
|
|
111
|
+
(C1-23, field evaluation #9). One SEC-004 appeared three times for three call
|
|
112
|
+
sites in one class and `by_band` was an observation census under a defect
|
|
113
|
+
label — measured on BroadleafCommerce, 20 rows over 15 distinct `defect_id`
|
|
114
|
+
beside `spring-audit`'s own 15 defects / 20 findings. `build_risk` now iterates
|
|
115
|
+
`defect_identity.group_by_defect`, so one row is one thing to fix and both
|
|
116
|
+
commands report the same count (asserted against each other, not against a
|
|
117
|
+
literal). Nothing is dropped: `witness_count` and `witness_sites` carry where
|
|
118
|
+
each observation was seen, `total_findings` publishes the census under its own
|
|
119
|
+
name, and `rule_id` becomes **`rule_ids`** because one defect can be witnessed
|
|
120
|
+
by TX-001 and TX-006 at once and a singular field would have to pick one.
|
|
121
|
+
`severity` is the strongest across the witnesses, and
|
|
122
|
+
`factors.defect_severity.basis` says so.
|
|
123
|
+
|
|
124
|
+
- **A Maven build property was published as a runtime activation** (C1-22, field
|
|
125
|
+
evaluation #9 — the row it called the most expensive in its report). Two
|
|
126
|
+
`<properties>` entries setting `spring.profiles.active` made
|
|
127
|
+
`posture --resolve-environments` answer `artefacts_disagree` — *"different
|
|
128
|
+
deployments, pick one"* — on a repository where **nothing activates a profile at
|
|
129
|
+
run time**. The honest verdict, which the command already implements, is
|
|
130
|
+
`NOT DECIDED IN THIS REPOSITORY`: the default set applies and the permissive
|
|
131
|
+
profile wins, which is exactly where that audit's most severe finding lived. The
|
|
132
|
+
command turned a fail-open into a deployment ambiguity, in the scenario it is
|
|
133
|
+
sold for. A build property is now admitted as an activation only when a resource
|
|
134
|
+
in the repository consumes it (`@spring.profiles.active@`, or
|
|
135
|
+
`${spring.profiles.active}` where the build declares its resources filtered).
|
|
136
|
+
Unconsumed, the value is kept — it is still the profile set a build of it would
|
|
137
|
+
produce, so the worst case keeps ranking it — and carries the reason it decides
|
|
138
|
+
nothing, beside a `build_properties_not_consumed` count. The positive control is
|
|
139
|
+
half the battery: before this, the case where the property genuinely does decide
|
|
140
|
+
produced the identical verdict, which is how the defect stayed invisible. No
|
|
141
|
+
movement on the field repositories.
|
|
142
|
+
|
|
143
|
+
- **A rule that disappeared was reported as protection gained.** Found by the
|
|
144
|
+
negative control, not in the field: delete a security configuration and every
|
|
145
|
+
request it decided goes `permit_all` → undecided, which a two-bucket
|
|
146
|
+
opened/closed split published as *"2 requests no longer reachable
|
|
147
|
+
unauthenticated"*. Four buckets now: `opened` and `closed` (attributable to the
|
|
148
|
+
change), `no_longer_decided` (coverage lost) and `now_open_previously_undecided`
|
|
149
|
+
(our reading moved, not necessarily the system — it never turns a gate red on
|
|
150
|
+
its own). Requests undecided on either side are counted in
|
|
151
|
+
`access_not_decided_either_side`, the delta publishes `direction`, and
|
|
152
|
+
`gate.reading` states the floor (I-8).
|
|
153
|
+
- **`git_checkout` no longer offers a remedy half its callers do not have.** The
|
|
154
|
+
not-a-repository message told every caller to "pass two checkouts positionally";
|
|
155
|
+
`posture` takes none. The message states the fact and each command supplies its
|
|
156
|
+
own hint.
|
|
157
|
+
|
|
5
158
|
## [4.1.0] — 2026-08-03
|
|
6
159
|
|
|
7
160
|
Minor, not a patch, and the criterion is this project's own: a new rule can trip
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.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
|
|
@@ -156,6 +156,10 @@ ask posture /path/to/repo --resolve-environments
|
|
|
156
156
|
# writing it, and populate .ask/contracts.yml from the measurement instead of by hand.
|
|
157
157
|
ask verify /path/to/repo --init
|
|
158
158
|
|
|
159
|
+
# Same derivation under a read-only constraint: prints what it would declare and
|
|
160
|
+
# writes nothing — the form an audit of somebody else's repository can run.
|
|
161
|
+
ask verify /path/to/repo --init --dry-run
|
|
162
|
+
|
|
159
163
|
# What each defect actually costs once reach, access and write effect are in it:
|
|
160
164
|
# severity × reachability × auth_verdict × write_effect, ordered, every factor traceable.
|
|
161
165
|
ask risk /path/to/repo
|
|
@@ -230,6 +234,9 @@ What a profile set **actually wires**: which conditional beans register, which d
|
|
|
230
234
|
`ask posture` · `ask posture --diff dev:prod` · `ask posture --property k=v`
|
|
231
235
|
→ [posture.md](docs/posture.md)
|
|
232
236
|
|
|
237
|
+
**`--diff-ref` makes it a gate instead of a report.** `--diff` moves the profile set; `--diff-ref origin/main:HEAD` (or `--since origin/main`, which compares a ref with the working tree) moves the *tree* under one profile set and answers *did this branch open an endpoint*. `--fail-on opened` exits 1 when it did. Both refs are read with `git archive` — nothing is written to the repository. The comparison is keyed on the request (`METHOD path`), counts requests only one side serves, and never reports a rule that disappeared as protection gained.
|
|
238
|
+
`ask posture . --since origin/main --fail-on opened`
|
|
239
|
+
|
|
233
240
|
**`--resolve-environments` carries it past the edge of the repository.** Every answer above
|
|
234
241
|
is conditional on a profile set, and which set runs is decided by the deployment. This flag
|
|
235
242
|
reads every artefact in the tree that can set `spring.profiles.active` — Maven/Gradle,
|
|
@@ -354,6 +361,13 @@ Two more boundaries worth stating in the same voice:
|
|
|
354
361
|
| **Applying a migration — nothing here edits source.** | This is the diagnosis layer: it measures what must change and what each change would reach. Rewriting code is an execution problem with an established executor, and duplicating it would mean maintaining a second, worse one. | OpenRewrite. `migrate-check` publishes the recipe each finding carries (`recipes[]`), which is the input that executor takes. |
|
|
355
362
|
| **What the process environment sets at start-up — the profile set, properties and secrets a container is given.** | Nothing in a repository can observe the environment of a process that has not started. A value read from a file here is the default the repository ships, never a guarantee of what runs. | `ask posture --resolve-environments` reads every artefact in the repository that names the profile set, says when none of them decides it, and ranks the admissible sets by what each leaves open. |
|
|
356
363
|
|
|
364
|
+
|
|
365
|
+
And one about the endpoint surface itself:
|
|
366
|
+
|
|
367
|
+
| Not covered | Why | What answers it |
|
|
368
|
+
|---|---|---|
|
|
369
|
+
| **HTTP surface that is not a Spring handler mapping — a servlet declared in `web.xml` or registered as a `ServletRegistrationBean`, a container-level mapping, a static console mounted by a library.** | The endpoint population is projected from the Spring route model: one entry per handler mapping. A servlet is reachable over HTTP and is not in that model, so it is absent from `endpoints`, from the access verdicts derived from it, and from every ratio keyed on the total. Merging a second population into that count would move a published figure and every ratio with it, so the population is declared before it is widened. | Read the deployment descriptors for `<servlet-mapping>` entries and the Spring configuration for `ServletRegistrationBean` beans. Field evaluation #9 found a monitoring console mounted this way, behind a credential `ask spring-audit` did report (SEC-007) at a path nothing here listed. |
|
|
370
|
+
|
|
357
371
|
**Positioning.** Until an executor ships, this is the **diagnosis layer**: it measures what
|
|
358
372
|
must change, what each change reaches, and what a gate should block — and it removes none of
|
|
359
373
|
it. Field evaluation scored it 7/10 as a report generator and 5.5/10 as a development tool,
|
|
@@ -118,6 +118,10 @@ ask posture /path/to/repo --resolve-environments
|
|
|
118
118
|
# writing it, and populate .ask/contracts.yml from the measurement instead of by hand.
|
|
119
119
|
ask verify /path/to/repo --init
|
|
120
120
|
|
|
121
|
+
# Same derivation under a read-only constraint: prints what it would declare and
|
|
122
|
+
# writes nothing — the form an audit of somebody else's repository can run.
|
|
123
|
+
ask verify /path/to/repo --init --dry-run
|
|
124
|
+
|
|
121
125
|
# What each defect actually costs once reach, access and write effect are in it:
|
|
122
126
|
# severity × reachability × auth_verdict × write_effect, ordered, every factor traceable.
|
|
123
127
|
ask risk /path/to/repo
|
|
@@ -192,6 +196,9 @@ What a profile set **actually wires**: which conditional beans register, which d
|
|
|
192
196
|
`ask posture` · `ask posture --diff dev:prod` · `ask posture --property k=v`
|
|
193
197
|
→ [posture.md](docs/posture.md)
|
|
194
198
|
|
|
199
|
+
**`--diff-ref` makes it a gate instead of a report.** `--diff` moves the profile set; `--diff-ref origin/main:HEAD` (or `--since origin/main`, which compares a ref with the working tree) moves the *tree* under one profile set and answers *did this branch open an endpoint*. `--fail-on opened` exits 1 when it did. Both refs are read with `git archive` — nothing is written to the repository. The comparison is keyed on the request (`METHOD path`), counts requests only one side serves, and never reports a rule that disappeared as protection gained.
|
|
200
|
+
`ask posture . --since origin/main --fail-on opened`
|
|
201
|
+
|
|
195
202
|
**`--resolve-environments` carries it past the edge of the repository.** Every answer above
|
|
196
203
|
is conditional on a profile set, and which set runs is decided by the deployment. This flag
|
|
197
204
|
reads every artefact in the tree that can set `spring.profiles.active` — Maven/Gradle,
|
|
@@ -316,6 +323,13 @@ Two more boundaries worth stating in the same voice:
|
|
|
316
323
|
| **Applying a migration — nothing here edits source.** | This is the diagnosis layer: it measures what must change and what each change would reach. Rewriting code is an execution problem with an established executor, and duplicating it would mean maintaining a second, worse one. | OpenRewrite. `migrate-check` publishes the recipe each finding carries (`recipes[]`), which is the input that executor takes. |
|
|
317
324
|
| **What the process environment sets at start-up — the profile set, properties and secrets a container is given.** | Nothing in a repository can observe the environment of a process that has not started. A value read from a file here is the default the repository ships, never a guarantee of what runs. | `ask posture --resolve-environments` reads every artefact in the repository that names the profile set, says when none of them decides it, and ranks the admissible sets by what each leaves open. |
|
|
318
325
|
|
|
326
|
+
|
|
327
|
+
And one about the endpoint surface itself:
|
|
328
|
+
|
|
329
|
+
| Not covered | Why | What answers it |
|
|
330
|
+
|---|---|---|
|
|
331
|
+
| **HTTP surface that is not a Spring handler mapping — a servlet declared in `web.xml` or registered as a `ServletRegistrationBean`, a container-level mapping, a static console mounted by a library.** | The endpoint population is projected from the Spring route model: one entry per handler mapping. A servlet is reachable over HTTP and is not in that model, so it is absent from `endpoints`, from the access verdicts derived from it, and from every ratio keyed on the total. Merging a second population into that count would move a published figure and every ratio with it, so the population is declared before it is widened. | Read the deployment descriptors for `<servlet-mapping>` entries and the Spring configuration for `ServletRegistrationBean` beans. Field evaluation #9 found a monitoring console mounted this way, behind a credential `ask spring-audit` did report (SEC-007) at a path nothing here listed. |
|
|
332
|
+
|
|
319
333
|
**Positioning.** Until an executor ships, this is the **diagnosis layer**: it measures what
|
|
320
334
|
must change, what each change reaches, and what a gate should block — and it removes none of
|
|
321
335
|
it. Field evaluation scored it 7/10 as a report generator and 5.5/10 as a development tool,
|
|
@@ -127,13 +127,21 @@ def bfs_callers(
|
|
|
127
127
|
max_depth: int,
|
|
128
128
|
impl_graph: object | None = None,
|
|
129
129
|
method_scoped: bool = False,
|
|
130
|
-
) -> tuple[list[str], list[str], bool, int]:
|
|
130
|
+
) -> tuple[list[str], list[str], bool, int, int]:
|
|
131
131
|
"""Walk the reverse graph from *seed_fqns*.
|
|
132
132
|
|
|
133
|
-
Returns ``(direct, indirect, was_truncated, self_excluded)``.
|
|
134
|
-
depth 1 — the callers of the seeds themselves; ``indirect`` is
|
|
135
|
-
beyond, up to *max_depth*. ``was_truncated`` says the hub-class
|
|
136
|
-
the walk, which makes the result a floor.
|
|
133
|
+
Returns ``(direct, indirect, was_truncated, self_excluded, guard_population)``.
|
|
134
|
+
``direct`` is depth 1 — the callers of the seeds themselves; ``indirect`` is
|
|
135
|
+
depth 2 and beyond, up to *max_depth*. ``was_truncated`` says the hub-class
|
|
136
|
+
guard capped the walk, which makes the result a floor.
|
|
137
|
+
|
|
138
|
+
``guard_population`` is the number the guard actually read: unique caller
|
|
139
|
+
**reference sites**, method-level keys included, before anything is
|
|
140
|
+
normalised to a class. It is returned rather than left implicit because the
|
|
141
|
+
figure the payload publishes is the class count, and a message that names
|
|
142
|
+
the cap without naming its own population contradicts the payload beside it —
|
|
143
|
+
measured on openmrs-core, *"symbol has > 500 direct callers"* printed next to
|
|
144
|
+
`direct_caller_count: 215` (C2-21).
|
|
137
145
|
|
|
138
146
|
A class's own members are members, not external callers, so they are dropped
|
|
139
147
|
and counted (BUG #2 — leaving them in inflated a blast radius ~12×). For a
|
|
@@ -170,7 +178,8 @@ def bfs_callers(
|
|
|
170
178
|
if etype not in SKIP_EDGE_TYPES:
|
|
171
179
|
unique_direct.update(c for c in fqn_list if not _is_self_reference(c))
|
|
172
180
|
|
|
173
|
-
|
|
181
|
+
guard_population = len(unique_direct)
|
|
182
|
+
effective_depth = 1 if guard_population > CALLER_CAP else max_depth
|
|
174
183
|
if effective_depth < max_depth:
|
|
175
184
|
was_truncated = True
|
|
176
185
|
|
|
@@ -202,4 +211,4 @@ def bfs_callers(
|
|
|
202
211
|
else:
|
|
203
212
|
_add_caller(caller, depth)
|
|
204
213
|
|
|
205
|
-
return direct, indirect, was_truncated, self_excluded
|
|
214
|
+
return direct, indirect, was_truncated, self_excluded, guard_population
|
|
@@ -115,6 +115,30 @@ class CanonicalSecurity:
|
|
|
115
115
|
# CanonicalEndpoint
|
|
116
116
|
# ---------------------------------------------------------------------------
|
|
117
117
|
|
|
118
|
+
def request_identity(method: str, path: str) -> str:
|
|
119
|
+
"""The identity of the **request**, not of the handler that serves it.
|
|
120
|
+
|
|
121
|
+
An endpoint `id` identifies a handler: it carries the controller FQN and the
|
|
122
|
+
handler symbol, so moving a mapping to another class produces a different
|
|
123
|
+
`id` for the same URL. That is the right identity inside one tree and the
|
|
124
|
+
wrong one across two — a comparison keyed on it reports every refactored
|
|
125
|
+
handler as one endpoint removed and another added, and the reader asking
|
|
126
|
+
"did this change what a caller can reach" has to subtract the noise by hand.
|
|
127
|
+
|
|
128
|
+
Two states of one repository — `contract-diff` between refs, `posture
|
|
129
|
+
--diff-ref` — compare what a client can call, which is METHOD + path. One
|
|
130
|
+
authority for it, because two spellings of one identity is how two commands
|
|
131
|
+
come to disagree about the same endpoint (ADR-0008 R11).
|
|
132
|
+
|
|
133
|
+
Note what this deliberately does *not* do: it does not normalise path
|
|
134
|
+
variable names or inline regexes. `/{id:.*}` and `/{id:[^0-9].*}` are
|
|
135
|
+
different URLs to a client, and equating them would hide a real change in
|
|
136
|
+
the surface — measured on a fleet repository where exactly that edit moved
|
|
137
|
+
93 requests.
|
|
138
|
+
"""
|
|
139
|
+
return f"{str(method).upper()} {path}"
|
|
140
|
+
|
|
141
|
+
|
|
118
142
|
@dataclass
|
|
119
143
|
class CanonicalEndpoint:
|
|
120
144
|
"""Canonical endpoint entity — single source of truth for REST endpoint data.
|
|
@@ -159,6 +183,11 @@ class CanonicalEndpoint:
|
|
|
159
183
|
"""Deterministic endpoint ID — stable across formatting/body changes."""
|
|
160
184
|
return f"{method}:{path}:{controller_class}:{handler_symbol}"
|
|
161
185
|
|
|
186
|
+
@property
|
|
187
|
+
def request_key(self) -> str:
|
|
188
|
+
"""This endpoint's request identity — see `request_identity`."""
|
|
189
|
+
return request_identity(self.method, self.path)
|
|
190
|
+
|
|
162
191
|
|
|
163
192
|
# ---------------------------------------------------------------------------
|
|
164
193
|
# CanonicalRepositoryIR
|