sourcecode 5.8.2__tar.gz → 5.8.4__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-5.8.2 → sourcecode-5.8.4}/CHANGELOG.md +142 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/PKG-INFO +4 -3
- {sourcecode-5.8.2 → sourcecode-5.8.4}/README.md +2 -2
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/DEFECT-LEDGER.md +21 -9
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/DEVELOPMENT-ROADMAP.md +296 -6
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/USER_GUIDE.md +1 -1
- {sourcecode-5.8.2 → sourcecode-5.8.4}/pyproject.toml +5 -1
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/__init__.py +1 -1
- sourcecode-5.8.4/src/sourcecode/build_modules.py +427 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/caller_reach.py +152 -7
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/canonical_ir.py +10 -1
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/change_plan.py +34 -1
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/cli.py +114 -49
- sourcecode-5.8.4/src/sourcecode/empty_reach.py +362 -0
- sourcecode-5.8.4/src/sourcecode/endpoint_disposition.py +235 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/hibernate_strat.py +245 -3
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/perf.py +59 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/phased_run.py +13 -4
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/posture.py +23 -1
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/pr_impact.py +129 -1
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/progress.py +19 -4
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/repository_ir.py +407 -27
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/risk.py +29 -8
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/risk_model.py +42 -2
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/schema_registry.py +16 -9
- sourcecode-5.8.4/src/sourcecode/schemas/core-analysis-v1.schema.json +98 -0
- sourcecode-5.8.4/src/sourcecode/schemas/endpoints-v1.schema.json +159 -0
- sourcecode-5.8.4/src/sourcecode/schemas/pr-impact-v1.schema.json +143 -0
- sourcecode-5.8.4/src/sourcecode/schemas/verify-edit-v1.schema.json +138 -0
- sourcecode-5.8.4/src/sourcecode/schemas/verify-v1.schema.json +132 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/serializer.py +32 -3
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_impact.py +83 -192
- {sourcecode-5.8.2 → sourcecode-5.8.4}/.github/workflows/build-windows.yml +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/.github/workflows/perf-gate.yml +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/.gitignore +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/.ruff.toml +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/CLAUDE.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/CONTRIBUTING.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/LICENSE +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/NOTICE +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/SECURITY.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/CACHE.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/COMMERCIAL-OFFER.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/CUSTOMER-VALIDATION.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/DEMO-5MIN.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/EXECUTION-PLAN-12MO.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/EXECUTIVE-ROADMAP.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/MANUAL-USUARIO.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/MODERNIZATION.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/PRODUCT_AUDIT.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/PRODUCT_IDENTITY.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/PRODUCT_TIERS.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/RC1-CHECKLIST.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/RELEASE-CHECKLIST.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/CATALOG-retrieval-intents.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-knowledge-provider-integration.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-p5.3b-statement-surface.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-p5.5-span-index.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-p6-semantic-impact-engine.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-p6-semantic-integration-engine.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-p7-semantic-services.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-robustness-security-inference.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/DESIGN-semantic-retrieval.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/EPV-p6-class-typeref.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/REPORT-p6-flow-analyzer-comparison.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/REPORT-p6-integration-detector-comparison.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/REPORT-p8-flow-analyzer-migration.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/REPORT-p9-integration-detector-migration.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0001-semantic-ir.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0002-source-evidence-boundary.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0003-architectural-characterization.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0004-semantic-retrieval.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0005-evidence-reconciliation.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0006-performance-harness.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0007-composition-semantics.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/adr/0008-answer-coherence.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/consumer-disposition-inventory.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/overview.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/product-architecture-review.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/product-strategy-layering.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/semantic-ir-consumer-matrix.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/architecture/semantic-ir-roadmap.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/baseline-ci.yml +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/commercial-pipeline-audit.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/contracts-ci.yml +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/contracts.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-15-petclinic-rest-issue11-vets-pagination.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-15-petclinic-rest-issue147-validation.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-15-petclinic-rest-workflow.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-15-phase18-openapi-surface.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-15-phase20-validation-surface.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-16-broadleaf-checkout-impact-fieldtest.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-16-petclinic-rest-issue11-weakness2-closed.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/eval/2026-06-16-spring-petclinic-issue2333-dead-vets.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/licensing-schema.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/migrate-check.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/README.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/REGRESSION-GATE.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/SUMMARY.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/alfresco__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/alfresco__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/broadleaf__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/broadleaf__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/eureka__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/eureka__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/jenkins__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/jenkins__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/jobrunr__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/jobrunr__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/keycloak__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/keycloak__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/neo4j__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/neo4j__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/ofbiz__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/ofbiz__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/openmrs__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/openmrs__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/petclinic__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.15/petclinic__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/SUMMARY.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/alfresco__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/alfresco__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/broadleaf__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/broadleaf__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/eureka__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/eureka__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/jenkins__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/jenkins__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/jobrunr__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/jobrunr__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/keycloak__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/keycloak__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/neo4j__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/neo4j__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/ofbiz__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/ofbiz__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/openmrs__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/openmrs__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/petclinic__ask__cold.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/2.5.16/petclinic__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/README.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/broadleaf__ask__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/broadleaf__endpoints__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/broadleaf__migrate-check__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/broadleaf__posture__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/broadleaf__spring-audit__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/gate-latest/broadleaf__validation__warm.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/perf/baselines/reference-best/README.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/posture.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/privacy.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/pro-experience-audit.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/schema.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/spec/envelope-v1.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/docs/spring-audit-ci.yml +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/scripts/compare_integration_engines.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/scripts/customer_smoke_test.sh +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/scripts/generate_jdk_exports.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/scripts/perf_gate.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/scripts/perf_harness.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/scripts/sync_gate_anchors.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/adaptive_scanner.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/archetype.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/architectural_baseline.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/architectural_delta.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/architecture_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/architecture_summary.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/ast_extractor.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/audit_report.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/baseline_autocapture.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/breaking_changes.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/bundled_docs.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/cache.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/cache_model.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/call_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/caller_metrics.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/chain_rules.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/cir_graphs.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/classifier.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/client_calls.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/code_notes_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/compare.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/confidence_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/constraint_diff.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/consumer_join.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/container_wiring.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/context_cache.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/context_graph.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/context_scorer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/context_summarizer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/contract_diff.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/contract_init.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/contract_model.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/contract_pipeline.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/coverage_parser.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/data_exposure.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/data_labels.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/declarations.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/defect_identity.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/degradation.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/dependency_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/deployment_prefix.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detach.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/__init__.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/base.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/csproj_parser.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/dart.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/dotnet.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/elixir.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/go.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/heuristic.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/hybrid.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/java.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/jvm_ext.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/nodejs.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/parsers.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/php.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/project.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/python.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/ruby.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/rust.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/systems.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/terraform.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/detectors/tooling.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/doc_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/dynamic_argument_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/endpoint_literals.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/endpoint_metrics.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/entrypoint_classifier.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/env_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/envelope.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/environment_resolution.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/error_schema.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/evidence_provider.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/execution_plan.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/explain.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/facts/__init__.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/facts/registry.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/file_chunker.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/file_classifier.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/filter_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/format_contract.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/fqn_utils.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/gate_anchors.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/git_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/git_checkout.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/graph_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/graph_evidence.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/identity_fallback.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/integration_coordinates.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/jdk_exports.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/license.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/manifest_cache.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/__init__.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/dependency_import.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/onboarding/applier.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/onboarding/backup.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/onboarding/detector.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/onboarding/planner.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/orchestrator.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/registry.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/runner.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp/server.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/mcp_nudge.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/metrics_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/migrate_check.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/migration_blast.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/non_coverage.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/openapi_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/openrewrite_recipe.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/output_bounds.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/output_budget.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/output_ceiling.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/output_encoding.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/output_sink.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/parallel.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/parse_cache.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/partial_contract.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/path_admission.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/path_filters.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/pipe_contract.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/pr_comment_renderer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/prepare_context.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/product_info.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/provenance.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/ranking_engine.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/readiness_timeline.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/readonly.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/reconciliation.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/redactor.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/reference_facts.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/release_info.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/relevance_scorer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/remedies.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/rename_refactor.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/repo_classifier.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/__init__.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/context.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/errors.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/executor.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/planner.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/query.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/request.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/resolution.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/result.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/retriever.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/runtime.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps_endpoint.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps_graph.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps_impact.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps_intf.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps_struct.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/retrieval/steps_txsec.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/ris.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/rule_catalog.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/rule_pass.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/runs.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/runtime_classifier.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/sarif.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/scanner.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/schema.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/schemas/envelope-v1.schema.json +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/security_chain.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/security_config.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/security_config_scan.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/security_posture.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/semantic_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/semantic_impact_engine.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/semantic_integration_engine.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/semantic_services.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/servlet_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/source_text.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_event_topology.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_findings.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_model.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_profiles.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_properties.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_security_audit.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_semantic.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/spring_tx_analyzer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/sql_taint.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/summarizer.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/target_admission.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/telemetry/__init__.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/telemetry/config.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/telemetry/consent.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/telemetry/events.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/telemetry/filters.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/telemetry/transport.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/test_gap_ranking.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/test_sources.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/text_input.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/timeline.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/token_estimate.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/tree_utils.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/type_usage_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/validation_inference.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/validation_surface.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/verify_edit.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/verify_repo.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/verify_rules.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/version_check.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/waivers.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/src/sourcecode/workspace.py +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/functions/README.md +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/functions/get-license/index.ts +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/functions/telemetry/index.ts +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/sql/license_event_ordering.sql +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/sql/licensing_schema.sql +0 -0
- {sourcecode-5.8.2 → sourcecode-5.8.4}/supabase/sql/telemetry_events.sql +0 -0
|
@@ -2,6 +2,148 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.8.4] — 2026-08-17
|
|
6
|
+
|
|
7
|
+
*Four rows, and three of them were reported with the wrong cause — which is the
|
|
8
|
+
theme worth naming rather than the row count. `endpoints` printing nothing under
|
|
9
|
+
`ASK_PROGRESS=1` was not `endpoints`; the ceiling hint denying a `--compact` that
|
|
10
|
+
fits does not reproduce on this tree at all; and the ceiling row's real defect
|
|
11
|
+
was one command's guard, not the hint every command shares. Measuring before
|
|
12
|
+
fixing changed the answer three times out of four.*
|
|
13
|
+
|
|
14
|
+
*The fourth is a defect the field never reported and could not have: a Spring
|
|
15
|
+
handler written on one line was not read as a handler, and its mapping bound to
|
|
16
|
+
the next method in the class. It was found by re-measuring a row that had been
|
|
17
|
+
closed twice — the battery that closed it writes its fixture in exactly that
|
|
18
|
+
style, so it had been asserting over a 0-endpoint payload since it was written.
|
|
19
|
+
A green test certified a defect the field reproduced three cycles running.*
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **JSON Schema documents for the five shapes a CI gate consumes** (B19):
|
|
23
|
+
`verify-v1`, `verify-edit-v1`, `endpoints-v1`, `pr-impact-v1` and
|
|
24
|
+
`core-analysis-v1`. B6 made all 44 identifiers *resolvable*; the use case that
|
|
25
|
+
opened it was validation in CI, which needs the document, and only
|
|
26
|
+
`envelope-v1` had one. `with_published_document` goes 1 → 7. Each document is
|
|
27
|
+
validated in the suite against the payload its command actually emits, so one
|
|
28
|
+
that stops describing the answer fails the battery — cross-checking against a
|
|
29
|
+
second repository is what caught `verify-edit`'s verdict vocabulary, which is
|
|
30
|
+
`pass` / `break` / `unverified`. The two canonically-named documents sit
|
|
31
|
+
beside emitted values that are still bare numbers, deliberately: renaming an
|
|
32
|
+
emitted `schema_version` is an incompatible change, declared and not applied.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **A phase boundary is announced whatever the machine's speed** (P1-proc).
|
|
36
|
+
`endpoints` printed **nothing** under `ASK_PROGRESS=1` — the flag an operator
|
|
37
|
+
turns on to ask *"is this working, or is it hung?"* — while `posture` printed
|
|
38
|
+
1, `spring-audit` 2 and `risk` 5. It was instrumented all along: a phase was
|
|
39
|
+
announced by the heartbeat, which fires only once the interval has elapsed, so
|
|
40
|
+
the reported counts were measuring how slow each command is rather than how
|
|
41
|
+
well it reports. Entering a phase now prints at `start()` and at every
|
|
42
|
+
`update()`; the periodic line is still rate-limited. `endpoints` goes 0 → 2
|
|
43
|
+
phase lines, and on a 24 073-file repository the first arrives at
|
|
44
|
+
`elapsed=0.0s` instead of after five seconds of silence.
|
|
45
|
+
- **`repo-ir` answers to the ceiling everybody else answers to** (ASK-09
|
|
46
|
+
residue). Its stdout guard compared against a constant bound at import, so
|
|
47
|
+
`ASK_MAX_OUTPUT_TOKENS=0` — declared in `breaking-changes-v1` as restoring
|
|
48
|
+
pre-5.7.0 behaviour exactly, and offered by `repo-ir`'s own hint — left it
|
|
49
|
+
refusing while every other command printed. Its refusal also published no
|
|
50
|
+
`estimated_tokens`, `ceiling_tokens` or `estimate_basis`: the number it
|
|
51
|
+
refused on existed only inside an English sentence. Both now come from
|
|
52
|
+
`output_ceiling.exceeds`. **The hint half of ASK-09 / B16-sv does not
|
|
53
|
+
reproduce** — measured on the reporter's own subject, `tutorials`, with no
|
|
54
|
+
environment variable and the real ceiling — and closes with a battery that
|
|
55
|
+
overflows for real instead of forcing a ceiling of 1 over a small fixture.
|
|
56
|
+
- **A handler written on one line is a handler** (E-36). `@GetMapping("/orders")
|
|
57
|
+
public List<Order> orders() { … }` was read as an annotation *line*: the
|
|
58
|
+
method was never parsed, and the mapping bound to the next declaration in the
|
|
59
|
+
class instead — so the route was published on a method that does not serve it
|
|
60
|
+
and its real handler disappeared. `endpoints tutorials` (24 073 `.java`)
|
|
61
|
+
recovers five routes, 2 263 → 2 268, none lost; the other ten golden
|
|
62
|
+
repositories are byte-identical, because none of them writes handlers this
|
|
63
|
+
way. Found while re-measuring `ASK-09`, whose own fixture is written in that
|
|
64
|
+
style and had been asserting over a 0-endpoint payload.
|
|
65
|
+
|
|
66
|
+
## [5.8.3] — 2026-08-17
|
|
67
|
+
|
|
68
|
+
*The whole queue of an adversarial audit run against BroadleafCommerce, closed
|
|
69
|
+
one commit per row. The three P0 rows share a shape worth naming: each was a
|
|
70
|
+
**confident** answer — an empty blast radius published as "low-risk isolated
|
|
71
|
+
change", a reach that was complete only from where the walk happened to start,
|
|
72
|
+
a negative set derived by subtraction and wrong in more than half. None of them
|
|
73
|
+
was a missing feature. All three were the product speaking with more certainty
|
|
74
|
+
than its evidence carried, which is the one failure mode this ledger has a
|
|
75
|
+
standing rule against.*
|
|
76
|
+
|
|
77
|
+
*Three of the twelve rows were reported with the wrong mechanism, and measuring
|
|
78
|
+
before fixing changed the answer each time: `P0-2` was not hub pruning, `ASK-16`
|
|
79
|
+
was the CIR build at 90 % of the clock, and `C3-127`'s shared substrate is 19 %
|
|
80
|
+
here rather than the ~27 s the report attributed to it. The rows carry what was
|
|
81
|
+
measured, including where it contradicts the reporter.*
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
- **`impact` and `plan` publish `retest_gap`** — the cone population, how many
|
|
85
|
+
of its classes a test source reaches, and the difference. The evaluator called
|
|
86
|
+
*"78 components in the cone, 1 test covering them"* the most actionable QA
|
|
87
|
+
figure of the run, and it had to be composed by hand from two outputs.
|
|
88
|
+
- **The build's module graph, read from the build** — Maven and Gradle, so a
|
|
89
|
+
module-to-module answer no longer depends on inferring the graph from imports.
|
|
90
|
+
- **Three coverage rows the scan structurally could not see** (P1): 131
|
|
91
|
+
`<named-query>` declarations across 43 mapping documents that contributed 0 of
|
|
92
|
+
443 Hibernate findings because the scan admitted `.java` and nothing else; HQL
|
|
93
|
+
composed by a builder and handed on as a string, which no pattern needing a
|
|
94
|
+
literal beside a query call can see; and a condition annotation declared in a
|
|
95
|
+
dependency, which left 60 conditionally-registered beans reading as
|
|
96
|
+
unconditional. Each verdict stays in the safe direction — a name-derived
|
|
97
|
+
condition is always `unresolved`, never active and never inactive.
|
|
98
|
+
- **`pr-impact` publishes the negative set instead of leaving it to
|
|
99
|
+
subtraction** (P0-3). Three states with one authority — `affected`, `unknown`
|
|
100
|
+
with its reason, `unaffected` with its basis — partitioning the declared route
|
|
101
|
+
population. The deliverable is *"what NOT to review"*, and derived by
|
|
102
|
+
complement it was wrong on ≥54 % of the rows the audit checked, including
|
|
103
|
+
routes ASK itself called reachable in the same run.
|
|
104
|
+
|
|
105
|
+
### Fixed
|
|
106
|
+
- **One authority for what an empty blast radius may claim** (P0-1). `ask impact`
|
|
107
|
+
answered `direct_callers: []`, `confidence_score: 1.0`, `closure_complete:
|
|
108
|
+
true`, *"Low-risk isolated change"* on a Hibernate id generator that 135
|
|
109
|
+
entities bind by string — while `impact-chain`, in the same run, published
|
|
110
|
+
`confidence: low` and named the blind spot. `empty_reach` now owns the five
|
|
111
|
+
codes, their precedence, their words and the confidence they force, and both
|
|
112
|
+
commands read it. Measured on the audited commit: confidence 1.0 → 0.4,
|
|
113
|
+
`closure_complete` true → false. A stereotype bean with no callers keeps its
|
|
114
|
+
confident answer.
|
|
115
|
+
- **Reach is a property of the graph, not of where the walk started** (P0-2). A
|
|
116
|
+
callee reported 0 endpoints where its own caller reported 268. The reported
|
|
117
|
+
cause — hub pruning — was not the cause: two expansions were applied to the
|
|
118
|
+
seed and to nothing else. Both now live in `ReachExpansion`, applied at every
|
|
119
|
+
node by the traversal the two commands share. `CriteriaTranslator` 0 → 274,
|
|
120
|
+
`Money` 60 → 277, cost flat.
|
|
121
|
+
- **The subtype expansion narrows to the member; the DI bridge does not**
|
|
122
|
+
(C1-25) — the regression the first cut of P0-2 introduced, caught by the row
|
|
123
|
+
it reopened.
|
|
124
|
+
- **A write that could not finish leaves nothing behind, whoever did the
|
|
125
|
+
writing** (B15-res). Registration is about ownership, and the `register_
|
|
126
|
+
artifact` call sat inside the branch that writes the file itself, so the three
|
|
127
|
+
commands supplying their own writer left a `.partial` in the caller's
|
|
128
|
+
repository and cleanup ran against an empty registry. The auditor had cleaned
|
|
129
|
+
the tree by hand four sessions running.
|
|
130
|
+
- **`--min-band` accepts the tier vocabulary our own report teaches** (B18) —
|
|
131
|
+
a regression from the `B2` fix: `audit-report` printed *"Filter tiers with
|
|
132
|
+
--min-band"* and `--min-band R1` answered `INVALID_INPUT`. `band_of` resolves
|
|
133
|
+
either spelling from the crosswalk the report is already rendered from.
|
|
134
|
+
- **`spring-audit` says where its wall clock goes** (ASK-16). 74 % of it was
|
|
135
|
+
outside the instrumented phase, and `analysis_time_ms` was never the analysis
|
|
136
|
+
— it was the security rule pass alone. `PhaseTimings` is always on and always
|
|
137
|
+
published, with the measured total and `unaccounted_ms` stated rather than
|
|
138
|
+
distributed over the phases. The missing 74 % is the CIR build (8 679 of
|
|
139
|
+
9 690 ms cold), so the optimisation target is the parse, not the rules.
|
|
140
|
+
- **A ledger row that shipped closed and read `open` for a day.** `B18`'s
|
|
141
|
+
verdict was written into the *defect* cell, because the row quoted an error
|
|
142
|
+
message carrying escaped pipes and the column count was checked by eye; two
|
|
143
|
+
older rows (`E-3`, `E-7`) were split the same way by a literal `|` inside a
|
|
144
|
+
quote. `tests/test_ledger_rows_are_wellformed.py` now asserts the layout —
|
|
145
|
+
five cells per row, and a verdict only ever in the last of them.
|
|
146
|
+
|
|
5
147
|
## [5.8.2] — 2026-08-17
|
|
6
148
|
|
|
7
149
|
*The five rows the 5.8.0 re-audits left in the queue, closed one commit at a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sourcecode
|
|
3
|
-
Version: 5.8.
|
|
3
|
+
Version: 5.8.4
|
|
4
4
|
Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/HarounDominique/sourcecode
|
|
6
6
|
Project-URL: Repository, https://github.com/HarounDominique/sourcecode
|
|
@@ -33,6 +33,7 @@ Requires-Dist: tree-sitter-javascript>=0.21; extra == 'ast'
|
|
|
33
33
|
Requires-Dist: tree-sitter-typescript>=0.21; extra == 'ast'
|
|
34
34
|
Requires-Dist: tree-sitter>=0.21; extra == 'ast'
|
|
35
35
|
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: jsonschema>=4.18; extra == 'dev'
|
|
36
37
|
Requires-Dist: mcp<2,>=1.2; extra == 'dev'
|
|
37
38
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
38
39
|
Requires-Dist: pytest-xdist>=3.8; extra == 'dev'
|
|
@@ -48,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
48
49
|
|
|
49
50
|
**Context · Impact · Migration · Architecture · Review — everything from one structural model.**
|
|
50
51
|
|
|
51
|
-

|
|
52
53
|

|
|
53
54
|
|
|
54
55
|
Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
|
|
@@ -136,7 +137,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
136
137
|
# pip / pipx
|
|
137
138
|
pipx install sourcecode # or: pip install sourcecode
|
|
138
139
|
|
|
139
|
-
ask version # ask 5.8.
|
|
140
|
+
ask version # ask 5.8.4 — and, on a build that has aged,
|
|
140
141
|
# how many releases have probably shipped since
|
|
141
142
|
```
|
|
142
143
|
|
|
@@ -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
|
Copyright (c) 2026 Dominique Haroun. All rights reserved. ASK Engine 5.7.0 and
|
|
@@ -92,7 +92,7 @@ brew tap haroundominique/sourcecode && brew install sourcecode
|
|
|
92
92
|
# pip / pipx
|
|
93
93
|
pipx install sourcecode # or: pip install sourcecode
|
|
94
94
|
|
|
95
|
-
ask version # ask 5.8.
|
|
95
|
+
ask version # ask 5.8.4 — and, on a build that has aged,
|
|
96
96
|
# how many releases have probably shipped since
|
|
97
97
|
```
|
|
98
98
|
|
|
@@ -41,15 +41,26 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
|
|
|
41
41
|
| B15 | **A write that could not finish left its temporary inside the caller's repository.** The payload goes to `.<name>.tmp-<pid>-<ns>` beside the destination and is renamed onto it — atomic, and correct — but when the rename failed nothing removed the temporary. Field matrix of five bad destinations on Windows: **13,9 MB of untracked files inside the repository** (2 757 334 B per attempt plus a 153 481 B `.partial`), as `??` in `git status`. Three edges came with it: a discard sink (`nul`, `NUL`, `CON`, `/dev/null`) was reported as an I/O error for successfully throwing output away; the OS `strerror` was interpolated into our English message, so the same matrix produced two `es-ES` messages and one English; and `-o ./missing/out.json` created the tree in silence while the hint told the caller to check that the directory exists. | 5.6.1 audit, re-reported and widened in the 5.7.2 audits | **High** — it is what breaks the read-only premise the product is sold on, and the auditor grades it blocking for any pre-CI security review | **closed 5.8.1** — one authority, `sourcecode.output_sink`. The temporary is removed in a **`finally`**, so the guarantee does not depend on enumerating the exit paths correctly (which is how five escape routes were missed). A discard sink answers `written: false`, `sink: "null-device"`, `exit 0`, is matched only on the platform that reserves the name (a POSIX file called `nul` is a file), and a run writing there keeps its checkpoint in the partials directory instead of `nul.partial`. The platform's message travels in `os_error: {raw, errno, errno_name, locale}`, never inside the contract sentence, and the error publishes `cleaned_up: [...]` — what the run took back with it, so "read-only" is checkable rather than trusted. Parent directories are created, deliberately, and the hint says so. `phased_run` writes through the same authority and registers its checkpoint as this run's, so a failed answer removes it. Regression `tests/test_output_sink_leaves_nothing_b15.py`, 12 assertions, including the end-to-end matrix leaving the tree byte-identical and a negative control that cleanup can only ever remove what this run wrote. |
|
|
42
42
|
| ASK-10 | **A command with an output ceiling and no flag that can get under it.** `posture spring-boot` returned 355 032 B / `exit 0` in 5.6.1; the unified ceiling made it `exit 1` — the right call — and left no in-band way out: not `--limit`, not `--top-n`, not `--min-severity`, not `--compact`. The only routes were `--output FILE` (the answer leaves the reader's context) and `ASK_MAX_OUTPUT_TOKENS=0` (the guard off). `exit 1` on **2 of 8** corpus repositories, and `thingsboard` returned `exit 0` in 5.7.2 — so the trap closes as the product improves: the growth was **better detection**, `unresolved` 64 120 → 184 864 B because conditional beans now name `@ConditionalOnExpression` instead of a generic reason. `validation` inherits it. | 5.7.2 audits, escalated to high and blocking by the 5.8.0 re-audit | **High** — structural rather than incidental: every future gain in exhaustiveness pushes another command at the ceiling | **closed 5.8.1** — `posture` and `validation` declare `--limit N` and `--compact`, both already in the ceiling's vocabulary, so the hint offers them without being taught (verified: with the ceiling forced, both now answer *"Use --compact …, --limit N …"*). Bounding is one authority, `sourcecode.output_bounds`: it walks the payload so a `--diff` answer is bounded on **both** sides, touches lists and never counts (`summary.unresolved` is an int under the same name), and every cut publishes the `*_cap` contract — `{total, shown, omitted}` plus the registered effect — because a flag that cuts without declaring the cut is B14 again. **Nothing is bounded by default**: a payload that shrinks silently between releases is what AS-16 was filed for. Measured: `posture keycloak-config-cli` 53 115 → 4 605 B under `--compact`, summary byte-identical. The invariant is now derived from the CLI registry (`tests/test_bounding_flag_registry_ask10.py`): a command that can exceed the ceiling and declares no bounding flag fails the battery until somebody decides, with `cli.COMMANDS_WITHOUT_BOUNDING` carrying the exemptions and their reasons — including, on the record, the repository-scale commands that have not yet been measured against the ceiling. Behaviour battery `tests/test_output_bounds_ask10.py`, 13 assertions. |
|
|
43
43
|
| B4 | **`verify-edit` reported two repository-wide builds beside two empty lists, and nothing in the payload could explain them.** Reported as the empty-diff short circuit failing: clean worktree, `changed_files: []`, `changed_build_files: []`, `head_model_cache_hit: true`, `model_built: true`, 23 468 ms with `ASK_PROGRESS` never leaving *diffing working tree vs HEAD*. | 5.6.1 audit, re-reported through the 5.7.2 and 5.8.0 audits | High | **closed 5.8.1 — and the reported cause is not the cause.** Measured on BroadleafCommerce (2 985 `.java`) with a clean tree: **0,47 s, `model_built: false`** — the short circuit (C3-32 / C3-102 / AS-13) fires, and the row's stated acceptance (`< 2 000 ms`, `model_built: false`, `verdict: pass`) already held. The reported shape reproduces exactly with **one untracked `zz.yml`**: both lists empty, `model_built: true`, **16,3 s**. `_can_move_a_verdict` admits any suffix an axis may read — deliberately, since C3-14 — so a `.yml` that is not `application*` is neither a changed `.java` nor a changed build file, and appeared in **no** published list. The change set was never empty; the payload could not say so. `analysis.model_forced_by: {count, paths, reason}` is that sentence, and `basis` no longer reads *"0 changed .java file(s) and 0 changed build file(s)"* beside two full builds. Regression `tests/test_verify_edit_names_what_forced_the_model_b4.py`, 5 assertions, including the negative control that a changed `.java` file is not explained twice. ⚠ Still open and now separable: the two-build cost itself when a forcing path exists (the parse cache serves the parse; the IR assembly is paid twice), tracked as the profiling half. |
|
|
44
|
-
| ASK-09 / B16-sv | **Three output-ceiling guards written outside the authority that owns them.** Reported as `endpoints`, `cold-start` and `posture` claiming *"No smaller inline variant is available with the current flags"* while `--compact` fits (10 194 B ≈ 2,5 K tokens). | 5.7.2 audits (corpus `ASK-09`, `saint-server` `B16-sv` — one row, two origin IDs) | High (corpus: medium) | **closed 5.8.
|
|
44
|
+
| ASK-09 / B16-sv | **Three output-ceiling guards written outside the authority that owns them.** Reported as `endpoints`, `cold-start` and `posture` claiming *"No smaller inline variant is available with the current flags"* while `--compact` fits (10 194 B ≈ 2,5 K tokens). | 5.7.2 audits (corpus `ASK-09`, `saint-server` `B16-sv` — one row, two origin IDs) | High (corpus: medium) | **closed 5.8.4 — does not reproduce, measured on the reporter's own subject, and the residue it was hiding is fixed.** `tutorials` (24 073 `.java`), no environment variable, the real 50K ceiling: `endpoints` answers *"Use --compact …, --limit N …"* at `estimated_tokens: 241 041`, `cold-start` answers *"Use --compact …"* at 230 518, and `posture` does not arise — 134 431 B ≈ 33K tokens, `exit 0`, no hint at all. Three cycles of byte-identical text were measured against a build, not against this tree. **What the row's rewritten acceptance did buy is the battery**: `tests/test_ceiling_hint_at_real_scale_ask09.py` builds a repository that genuinely overflows (1 200 handler mappings, ~107K tokens), *discovers* the population by running every path-only command that declares a bounding flag rather than listing them, and asserts that no such command denies a flag it declares — parametrised by (command, repository), never by (command, artificial ceiling). It is verified red: forcing the *"nothing smaller"* prefix fails it. It also refuses to be vacuous — the fixture must overflow, the population must contain the two commands the field named, and the flag the hint offers must actually get the answer under the ceiling when run. ⚠ **The residue the old fixture could not see**: `repo-ir`'s stdout guard compared against a constant bound at import, so `ASK_MAX_OUTPUT_TOKENS=0` — which `breaking-changes-v1` declares as restoring pre-5.7.0 behaviour *exactly*, and which `repo-ir`'s own hint offers — left it refusing while every other command printed, and its refusal published no `estimated_tokens`, no `ceiling_tokens` and no `estimate_basis`, so the number it refused on existed only inside an English sentence. Both now come from `output_ceiling.exceeds`, the authority: one refusal, one shape, one ceiling that the caller can actually move. Measured: `=0` now exits 0, a lowered ceiling bites, and the payload carries its measurement. **History, kept because the row was closed twice on it:** REOPENED 5.8.2 — the closure was verified against the wrong path: against the unified emit seam, with the ceiling forced, `endpoints` answers *"Use --compact …, --limit N …"* and `cold-start` *"Use --compact …"* — the derivation reads `ctx.command.params` and works. What did reproduce is the half the row itself names: `repo-ir` carried **two** hand-written hints (stdout and the separate MB guard on a written file) and `migrate-check` a **third**, `_migrate_check_output_hint` — a hand-maintained copy of `build_hint`, same list, same prefix rule. A hand-written escape list cannot know which flags the caller already passed, which is the reported defect seen from the other side. `output_ceiling` now owns the whole vocabulary: `--force` as a **bypass** (offered only where the command declares it), `--gzip` as a **file-only** bound, `ways_out(destination="file")` for the MB guard — where `--output FILE` is not an escape and the token env var does not apply — and the *"nothing smaller"* prefix decided by counting the **bounding** escapes rather than the sentence, so a command whose only remaining escape is `--force` no longer opens with "Use". `_migrate_check_output_hint` is deleted. Regression `tests/test_ceiling_hint_one_authority_ask09.py`, 7 assertions, two of which are structural: `cli.py` may not contain the prefix sentence, and every `OUTPUT_TOO_LARGE` hint in it must come from `build_hint` (AST-checked). ⚠ **Reopened by the 5.8.2 re-audits, which report it as `B16` (saint-server) and `ASK-09` (corpus) — one defect, two origin IDs, like `B16-sv` before it.** The fix landed and the *hint* half did not: `ask endpoints tutorials` (24 073 `.java`) still answers *"No smaller inline variant is available with the current flags"* at `estimated_tokens: 240 908`, while `--compact` (45 902 B ≈ 11,5 K) and `--limit 50` (66 765 B ≈ 16,7 K) both fit with 4× of margin and are both declared on `endpoints`. Byte-identical text since 5.7.2, third cycle. **The diagnosis is the valuable part and it is about our method, not the code**: the regression assertion forces a low ceiling (`ASK_MAX_OUTPUT_TOKENS=1`) over a *small* fixture payload, and the path that fails is the *natural* rebase of a 240 K-token payload against the real 50 K ceiling — so the battery validates one route while the defect lives in another, and a green test certified a defect the field reproduces in three consecutive cycles. `posture` received `--limit`/`--compact` in the same release this row was closed in and *does* answer correctly, which is why the derivation looked proven. Suspected: a short-circuit that decides the hint before `ctx.command.params` is consulted, reachable only at a size the fixture never reaches (compare `ASK_MAX_OUTPUT_TOKENS=2000 ask endpoints <small>` against `ask endpoints <24k-file repo>`). **Acceptance, restated so the same closure cannot repeat**: the hint names `--limit` and `--compact` with an unforced ceiling on a repository that genuinely exceeds 50 K, and the regression is parametrised by `(command, repository)` over a fixture that really overflows — never by `(command, artificial ceiling)`. |
|
|
45
45
|
| ASK-12 | **A field describing an exclusion named three production trees as excluded.** `spring-audit spring-boot` published `patterns_matched: [… "core/", "module/", "cli/" …]` — 4 254 + 1 625 + 76 production files. Read literally, whole modules had left the audit population; measured, the exclusion was *conservative* (3 650 excluded against 4 214 files actually under a test root). The auditor filed an over-exclusion false positive on the strength of the field and then withdrew it. Cause: the constructor's fallback named `parts[0]`, the first path segment, when no source-root marker matched — and the same published field had **two constructors** with different shapes and different `basis` strings (`cli.py` for `spring-audit`, `repository_ir.py` for `endpoints`) over the one authority both already shared. | 5.7.2 audits | Low — a reporting defect, not an exclusion one, which is why it cost an auditor a round instead of costing a finding | **closed 5.8.1** — one producer, `repository_ir.excluded_test_source_detail`, beside `is_test_source_file`, which is the authority that decides the fact. `patterns_matched` can now only hold a **declared test root** — a directory whose files are excluded in their entirety — and everything else is published as a `rules_matched` census with a `rules_meaning` sentence per rule, so a per-file rule can no longer be read as a whole-directory claim. A surface counting something other than files says so (`unit`, `files_matched`), because `endpoints` counts route annotations. Regression `tests/test_test_source_exclusion_one_producer_ask12.py`, 14 assertions, two of them structural: `cli.py` may not compose the key at all, and exactly one function in `repository_ir.py` may (AST-checked). |
|
|
46
46
|
| B14 | **`--compact` made the answer smaller by making it unattributable.** `cold-start --compact` took 1 968 807 characters to 16 452 (−99,2 %) and removed **11 of 19 keys** on the way — `schema_version`, `repo_id`, `current_git_head` and the 331-character `hotspots` among them — in a document whose weight was **97,7 % `endpoints`**, and cut the endpoint list to 30 with no `*_cap`. The view an agent is told to inject could not say which repository, which commit or which schema it described. | 5.6.1 audit; AS-15 closed the root `--compact` half in 5.7.0, this is the `cold-start` half, re-reported against 5.8.0 | High | **closed 5.8.1** — three rules, in the shared bound rather than invented per command. `COMPACT_IMMUTABLE_KEYS` is the minimum no `--compact` may remove (provenance is contract, not content). Every collection that was cut declares `{total, shown, omitted}` with the registered effect — measured on BroadleafCommerce, `endpoints_cap` now publishes `total: 272, shown: 30, omitted: 242` where the cut was silent. And trimming goes by **weight**: a section already under 2 KB is kept rather than dropped by a fixed list, which is what recovers `hotspots`; everything actually removed is named with its size in `compact_omitted`. Regression `tests/test_compact_keeps_identity_b14.py`, 12 assertions, including that nothing removed claims nothing. |
|
|
47
47
|
| B2 | **A risk band and a rule severity share a vocabulary and measure different things.** They disagreed on **34 of 50 rows (68 %)** — 5 defects rated `medium` band as `critical`, 29 rated `high` band as `medium` — and both numbers were right: severity is what the rule says about the defect, the band is what the composition says about it in *this* repository (reach, access, write effect). One surface on, the collision became a contradiction a reader could not resolve: `audit-report` announced **14 critical** over bands while `--min-severity critical` filtered severities and returned **0**. | 5.6.1 audit, reproduced through 5.8.0 | Medium | **closed 5.8.1** — the band now also has a name that cannot be mistaken for a severity: `risk_tier: R1…R5`, published **beside** `band` and never instead of it, so no consumer loses a field it reads. One mapping (`risk_model.tier`), one sentence (`TIER_CROSSWALK_NOTE`) travelling with every count — it names both axes, states that they disagree by construction, gives the crosswalk, and points at `--min-band` as the flag that filters tiers rather than `--min-severity`. `risk` publishes `by_tier` beside `by_band`; `audit-report` leads with tiers, keeps bands under the legacy spelling, renders the note, and derives tiers from bands when it is handed an older bundle. Regression `tests/test_risk_tier_is_not_a_severity_b2.py`, 12 assertions, including that no tier shares a spelling with any severity and that a summary with no census still says *not measured* rather than zero. ⚠ The row's `reach`/`access`/`score_raw` per row was already shipped (`factors`, `endpoints_reached`, `reach_scope`); `--min-band` already existed on `risk` and `enrich`. |
|
|
48
|
-
| B11 | **A boolean about the working tree was published without the unit it was measured in.** The field read `has_uncommitted_changes: false` in the same terminal where `git status --porcelain` had just printed `?? .claude/settings.json`, with no `_basis` and no unit beside it. The rule is right and stays: C1-27 decided that a change under an editor or agent state directory is not a change to the code, because no analyser opens those files, and counting it reported STALE on a snapshot that described the tree exactly with `Delta: 0` next to it. What the payload could not do was *say* it — with no basis, "no changes" and "changes I did not count" are the same three characters, and the only way to tell them apart was to read the source. | 5.6.1 audit, inherited | Low — a disclosure defect over a correct rule, which is why it cost a reader's trust rather than a wrong answer | **closed 5.8.
|
|
49
|
-
| P1 / F-BR | **Every comparison the release gate made was a ratio, and a cost that was always too high never regresses.** `validation` went 13,8 → 34,1 → 39,5 s across three releases with no single step over the 20 % threshold, so the gate was green at every step and the product shipped a staircase. The row also carried two prescribed cache-reuse assertions that the field **refuted with its own measurements** before either was implemented: `samples[0]/samples[-1] > 1,5` reads only the ends of the series and passes 5.6.1, and `median(samples)/min(samples) < 1,5` scores shape rather than cost and therefore awards **5.5.5, the worst release of the six, a perfect mark** — flat-and-slow (nothing cached, 80 s) and flat-and-fast (everything cached, 30 s) are the same ratio. | 5.7.2 audits (`P1`, `B7`), with the third formulation validated by the field against six measured releases | Medium — the detector gap that let a ×15,7 regression ship (C3-120), compounded by a documented instruction to build the wrong detector | **closed 5.8.
|
|
50
|
-
| ASK-11 | **A correct decision announced only by the error message of the run it broke.** The unified output ceiling turned `exit 0` into `exit 1` for invocations that returned data one minor earlier — `endpoints tutorials` (987 141 B ≈ 247 K tokens), `endpoints thingsboard` (318 380 B), `posture spring-boot` (355 032 B on 8 667 files). The decision is not in dispute and is not being revisited: 247 K tokens printed into an agent's context window was worse than a refusal. The defect is the **mode of disclosure** — the trigger is repository size rather than a flag, so an invocation that fits today stops fitting on a bigger repository or after a release that detects more, and the failure lands as `exit 1`, which stops a CI build. | 5.7.2 audits — filed by the field as *"cambio incompatible, no defecto"* | Medium — a release-policy defect: nothing was wrong with the run, only with what could be known before it | **closed 5.8.
|
|
51
|
-
| ASK-13 | **A floor that could be anything, in the sentence whose job is to size the work.** `cache warm tutorials` reported *"This repository: at least 8 418 Java files, cache warm"* about a repository holding **24 073** — a third of the truth — and *"at least 8 401"* on `spring-boot` against 8 667. The two land near each other for one reason: the walk stops after 25 000 **directory entries**, not after 25 000 Java files, so a tree with ordinary amounts of non-Java content exhausts the budget at roughly the same place however much Java it holds. `at least` was honest about the direction of the error and silent about its size, which is exactly what made it useless here. | 5.7.2 audits | Low — the count is a floor and says so; what it could not say was how far a floor it is | **closed 5.8.
|
|
52
|
-
| B6 | **Fourteen `schema_version` values in payloads and not one of them resolvable.** `ask schema verify-v1` answered *invalid* about a string this tool had just printed; `endpoints` emitted no version at all; and the spellings did not agree — `<domain>-v1` beside a bare `1.0`, a `1.1`, a `1.2`, a `1.0.0`, a `2.1` and a `0.1-experimental`. A version identifier a consumer cannot look up is a string, not a contract. | 5.6.1 audits, inherited | Medium — every consumer that pins a shape pins it on one of these | **closed 5.8.
|
|
48
|
+
| B11 | **A boolean about the working tree was published without the unit it was measured in.** The field read `has_uncommitted_changes: false` in the same terminal where `git status --porcelain` had just printed `?? .claude/settings.json`, with no `_basis` and no unit beside it. The rule is right and stays: C1-27 decided that a change under an editor or agent state directory is not a change to the code, because no analyser opens those files, and counting it reported STALE on a snapshot that described the tree exactly with `Delta: 0` next to it. What the payload could not do was *say* it — with no basis, "no changes" and "changes I did not count" are the same three characters, and the only way to tell them apart was to read the source. | 5.6.1 audit, inherited | Low — a disclosure defect over a correct rule, which is why it cost a reader's trust rather than a wrong answer | **closed 5.8.2** — the boolean now travels with the census that explains it. One producer, `baseline_autocapture.worktree_dirty_detail`: one `git status --porcelain -uall`, every path it prints classified once by `dirty_exclusion_reason`, and `{dirty, counted, excluded, excluded_by, basis}` out. `worktree_dirty` is that dict read as a boolean and `counts_as_dirty` is the same rule read as a predicate, so the three cannot drift — the C1-44 shape, kept. The `basis` names the command a reader can check it against and reconciles with it: `counted + excluded` is exactly what `git status` printed (asserted), and each exclusion carries its sentence from `EXCLUSION_REASONS` rather than a bare key. **`false` and "not checked" stopped being the same answer**: a non-git directory publishes *"not a git worktree: the working tree could not be compared to HEAD"* instead of a confident `false`, and a run whose own scan measured the dirtiness says which source answered. Published as `has_uncommitted_changes_basis` at every site that prints the boolean — `cold-start` and its `_cache` block (both CLI paths through `_cache_meta_block`, plus the RIS bootstrap), and the MCP `check_freshness` tool with its docstring corrected. Regression `tests/test_uncommitted_changes_basis_b11.py`, 14 assertions, including the reported pair end-to-end and the reconciliation against `git status`. |
|
|
49
|
+
| P1 / F-BR | **Every comparison the release gate made was a ratio, and a cost that was always too high never regresses.** `validation` went 13,8 → 34,1 → 39,5 s across three releases with no single step over the 20 % threshold, so the gate was green at every step and the product shipped a staircase. The row also carried two prescribed cache-reuse assertions that the field **refuted with its own measurements** before either was implemented: `samples[0]/samples[-1] > 1,5` reads only the ends of the series and passes 5.6.1, and `median(samples)/min(samples) < 1,5` scores shape rather than cost and therefore awards **5.5.5, the worst release of the six, a perfect mark** — flat-and-slow (nothing cached, 80 s) and flat-and-fast (everything cached, 30 s) are the same ratio. | 5.7.2 audits (`P1`, `B7`), with the third formulation validated by the field against six measured releases | Medium — the detector gap that let a ×15,7 regression ship (C3-120), compounded by a documented instruction to build the wrong detector | **closed 5.8.2** — a second axis, `perf.absolute_cost_report`, published under `absolute_cost` in the gate report and able to turn the verdict red on its own. Two assertions: a **declared ceiling per command** (`RELEASE_GATE_ABSOLUTE_WARM_MS`, each published beside the mark it was calibrated against at 5.7.2, because a limit nobody can audit against a measurement is just a number), and the **anchored** one the field validated — `max(sample) < 8 × posture_warm`, with the anchor measured *in the same run* so a slower runner moves the limit with it and the number stays a claim about the product rather than about the machine CI allocated. Scope is enforced rather than assumed: a ceiling calibrated on ~3 300 files takes no part on another corpus, and cold cells still do not decide (C3-108). **The two refuted formulations are carried in the source** (`REFUTED_REUSE_FORMULATIONS`) with the counter-example that killed each, and the battery replays both refutations plus the six-release validation table (`ANCHOR_MULTIPLE_VALIDATION`), so a later change to the multiple cannot quietly stop discriminating — the multiple is asserted to sit between the worst pass (6,2×) and the best fail (8,5×). `--self-test` gained two reds, because a green gate is evidence only when a red one is reachable on the same constants. Regression `tests/test_absolute_cost_gate_p1.py`, 25 assertions, including the staircase end-to-end: ratio axis green, release red. ⚠ The rest of F-BR — a synthetic ~200 KB / ~5 000-call fixture timing `build_sql_taint`, and warm cells for the composition family in the gate baseline — is **not** in this row and stays open; and C3-113 still applies, since the gate's release path is triggered by a tag and no release since `v5.2.0` carries one. |
|
|
50
|
+
| ASK-11 | **A correct decision announced only by the error message of the run it broke.** The unified output ceiling turned `exit 0` into `exit 1` for invocations that returned data one minor earlier — `endpoints tutorials` (987 141 B ≈ 247 K tokens), `endpoints thingsboard` (318 380 B), `posture spring-boot` (355 032 B on 8 667 files). The decision is not in dispute and is not being revisited: 247 K tokens printed into an agent's context window was worse than a refusal. The defect is the **mode of disclosure** — the trigger is repository size rather than a flag, so an invocation that fits today stops fitting on a bigger repository or after a release that detects more, and the failure lands as `exit 1`, which stops a CI build. | 5.7.2 audits — filed by the field as *"cambio incompatible, no defecto"* | Medium — a release-policy defect: nothing was wrong with the run, only with what could be known before it | **closed 5.8.2** — treated as the policy it is, not as a changelog line. `sourcecode.breaking_changes` declares the change with what a reader planning an upgrade actually needs: the exit code on both sides, the trigger, the **compatibility route** (`ASK_MAX_OUTPUT_TOKENS=0`, which restores pre-5.7.0 behaviour exactly, for the pipeline that is red right now) and the **remedy** (a bounding flag, or `--output FILE`, for when there is time) — in that order, because an upgrade note offering only the principled fix is one a reader skips. "A large repository" is given its sizes: the three field invocations are carried with their measured bytes. Three surfaces, all derived from that one declaration: `ask schema breaking-changes-v1` prints it, a `## Upgrading` section sits above the release history in the CHANGELOG, and **every affected command names the ceiling in its `--help`** — the population derived from the CLI registry (all commands minus `COMMANDS_WITHOUT_COPY`, the existing authority for "stdout is a confirmation, not an answer"; a confirmation cannot reach a 50K-token ceiling), never a hand-written list, since the list is exactly what a reader is told to trust. The battery enforces the **policy**: a declared change must move an exit code, carry both routes, name its invocations with measurements, and reach the changelog. Regression `tests/test_breaking_change_disclosure_ask11.py`, 95 assertions (one per affected command, parametrised from the live registry). ⚠ Found and fixed on the way: `version` published `registries: ["facts-v1"]` as a literal while `schema` derived its list, under a comment promising the two could not disagree — the second registry made them disagree. Both read `published_registries()` now. |
|
|
51
|
+
| ASK-13 | **A floor that could be anything, in the sentence whose job is to size the work.** `cache warm tutorials` reported *"This repository: at least 8 418 Java files, cache warm"* about a repository holding **24 073** — a third of the truth — and *"at least 8 401"* on `spring-boot` against 8 667. The two land near each other for one reason: the walk stops after 25 000 **directory entries**, not after 25 000 Java files, so a tree with ordinary amounts of non-Java content exhausts the budget at roughly the same place however much Java it holds. `at least` was honest about the direction of the error and silent about its size, which is exactly what made it useless here. | 5.7.2 audits | Low — the count is a floor and says so; what it could not say was how far a floor it is | **closed 5.8.2** — the second of the two outcomes the acceptance allows: the cap stays (this count runs while somebody waits for `--help` to render) and **names itself**. The message now reads *"at least 8 418 Java files (the scan stopped at 25 000 directory entries, so the repository holds more)"*, with the unit named, because "entries" and "Java files" being different units is the whole reason the number saturates where it does. A cap that did **not** bind is not mentioned at all — it is not part of that count's basis, and naming it would suggest it was. ⚠ **A second half that is not a message, found while measuring the first**: `Scope.large` answered `False` from a saturated count, so a repository whose first 25 000 entries held few sources was advised as *small* — and the class had already written the correct rule in prose (*"a capped count below it is not [enough]"*) while the code ignored it. `size_decided` now separates the two cases a floor supports: above the threshold a capped count still decides (more files only make `large` more true), below it decides nothing, and the cost verdict answers `unknown` with its reason rather than guessing the cheap regime. Also fixed on the way: `count_java_files` takes its cap as a **default argument**, bound at definition time, so `measure_scope` now passes it explicitly — otherwise the cap that bound the walk and the cap the message names could drift into a published falsehood. Regression `tests/test_scan_cap_names_itself_ask13.py`, 13 assertions, including a tree shaped like the field's (many entries, few sources). |
|
|
52
|
+
| B6 | **Fourteen `schema_version` values in payloads and not one of them resolvable.** `ask schema verify-v1` answered *invalid* about a string this tool had just printed; `endpoints` emitted no version at all; and the spellings did not agree — `<domain>-v1` beside a bare `1.0`, a `1.1`, a `1.2`, a `1.0.0`, a `2.1` and a `0.1-experimental`. A version identifier a consumer cannot look up is a string, not a contract. | 5.6.1 audits, inherited | Medium — every consumer that pins a shape pins it on one of these | **closed 5.8.2** — `sourcecode.schema_registry` names **every** `schema_version` this release can emit (50 entries over 44 distinct identifiers, 13 of them non-conforming) with what it versions, which module defines it, which commands carry it, and whether a JSON Schema document exists behind it. `ask schema <value>` resolves all of them: a documented identifier still prints its schema, and one without a document resolves to its entry rather than to a rejection — which is what it honestly is, a named versioned shape with no formal document yet, said plainly instead of implied away. `endpoints` gained `endpoints-v1`, the version it never had. **The population is checked against the source, not maintained by hand**: the battery walks `src/sourcecode` by AST and fails when a `schema_version` literal or a `*SCHEMA*` constant is not named by the registry, and it also verifies the registry's own claims — the module and constant each entry cites must really hold that value (this caught one wrong `source` while it was being written). **The finding the field could not have made from outside**: `1.0` is not a schema. **Seven** unrelated shapes spell their version that way — the root analysis, `impact-chain`, `pr-impact`, migration blast, Spring impact, event topology and test-gap ranking — so `resolve()` returns all seven and publishes the identifier as ambiguous rather than picking one, and every ambiguous entry must offer a canonical name to pin instead (asserted). **The `<domain>-v<N>` migration is declared, not applied**: renaming an emitted `schema_version` changes a payload consumers pin on, which is precisely the incompatible change `breaking-changes-v1` exists to stop us making in silence, so each non-conforming identifier carries the name it should become and the battery asserts none of them was quietly renamed in this release. `schema --help` documents the mapping and the `1.0` trap. Regression `tests/test_schema_registry_resolves_b6.py`, 66 assertions. ⚠ Writing the 40-odd JSON Schema documents themselves is **not** in this row and stays open; what closed is resolvability, which is what the row was filed for. |
|
|
53
|
+
| P0-1 | **`impact` publishes a confident zero on a class the container wires by string.** `ask impact IdOverrideTableGenerator . --depth 6` @ `8645873661` answers `direct_callers: []`, `risk_level: low`, `confidence_score: 1.0`, `closure_complete: true`, no `analysis_warnings`, *"No callers or dependents found … Low-risk isolated change."* — on a Hibernate id generator that 135 entities bind by string in `@GenericGenerator`, while `ask explain` lists **119 referencing classes** and `ask impact-chain`, in the same run, publishes `confidence: low` with the CH-005 blind spot named. Three commands, one symbol, three verdicts, and the unhedged one is the sentence a migration scope is cut with | Broadleaf adversarial audit 2026-08-17 (§13) | **High** — the failure mode that destroys the offer in one line: it excludes from scope the exact component a Hibernate migration turns on | **closed 5.8.3** (`90f10b2`) — the reading of an empty result is now one authority, `sourcecode.empty_reach`: the five codes (CH-005, CH-003, G-2, P1-B, P1-E), the precedence between them, the words each is published in and the confidence they force. Only the detectors stay with their substrate, and CH-003's moved whole because it only ever read graph nodes, which both callers hold. `impact` publishes `blind_spots` with the chain's codes, `analysis_warnings` with byte-identical text, `confidence_score` capped below the `medium` band, `closure_complete: false` with `unmodeled_inbound_edges` as the third cause beside E-32's two, and an explanation that states the blind spot instead of asserting isolation. Measured on the audited commit: confidence 1.0 → 0.4, `closure_complete` true → false, `blind_spots: [framework_di]` naming the Hibernate supertype. Negative controls hold — a stereotype bean with no callers keeps its confident answer. Battery `tests/test_empty_reach_authority_p01.py`, 14 assertions, including a cross-command invariant that fails if the two payloads disagree on the codes or on the words |
|
|
54
|
+
| P0-2 | **Endpoint reach is not monotone: a callee reports less than its caller.** `ask impact BasicPersistenceModule . --depth 6` → 268 endpoints; `ask impact CriteriaTranslator . --depth {6,8}` → **0**, with `BasicPersistenceModule` a direct caller of `CriteriaTranslator` and the walk reporting `closure_complete` for itself. Breaks the property a tech lead assumes without asking — if A calls B, the reach of B contains the reach of A — and invites excluding the core of the change | Broadleaf adversarial audit 2026-08-17 (§13) | **High** | **closed 5.8.3** (`41c3150`, `a6cbe21`) — **the reported cause is not the cause**: the hub-frontier heuristic expands its nodes in the main walk and the closure was genuinely exhausted. Two expansions applied to the SEED and to nothing else — the subtype expansion (E-27's rule) and the interface bridge for `implements` edges carrying an unresolved short name, which `ImplementationGraph` cannot see. Reach depended on where the walk started. Both now live in `caller_reach.ReachExpansion`, applied at every node by the traversal both commands share. Measured A/B, depth 6, same cache: `CriteriaTranslator` **0 → 274** endpoints, `BasicPersistenceModule` 268 → 274, `Money` 60 → 277, and `AdminSecurityFilter`/`IdOverrideTableGenerator`/`SkuCustomPersistenceHandler` unchanged at 0. Cost flat (0.09–0.16 s/query vs 0.08–0.23 s). `impact-chain` takes the same object: 0 → 239 endpoints at the same depth. ⚠ The subtype half narrows to the corresponding member and the DI bridge does not — see `C1-25`, which the first cut of this fix reopened. Battery `tests/test_reach_monotonicity_p02.py`, 8 assertions |
|
|
55
|
+
| P0-3 | **The negative set derived from `pr-impact` is wrong in ≥54 % and does not exist as a state.** `ask pr-impact . --files -` over the Hibernate surface → 258 affected endpoints; the complement against `ask endpoints` is 13, of which **3 are reachable according to ASK itself** (`impact-chain 'AdminBasicEntityController#viewEntityForm'` returns `/user-management/{id}`) and 4 more through `ProductHandlerMapping`/`CategoryHandlerMapping`/`SkuHandlerMapping` → `blCatalogService` → `ProductDaoImpl`, which is in the changed set | Broadleaf adversarial audit 2026-08-17 (§6.2, §13) | **High** — it is literally the deliverable (*"what NOT to review"*), and it would fail on the routes a QA lead checks first | **closed 5.8.3** (`1803ebb`) — three states with one authority (`sourcecode.endpoint_disposition`): `affected`, `unknown` (with the reason), `unaffected` (with its basis), partitioning the declared route population so nothing is subtracted. Three blockers demote `unaffected`, each repository-wide because none is a statement about one route: a container-wired class inside the cone (the audit's handler-mapping chain, caught through the `container_wiring` authority, never by a name), a closure that was not exhausted, a changed file that mapped to no class. Closing it needed two missing edges, both found by reproducing §6.2: **`super.m()` produced no call edge when the overriding class declared `m` itself** — which is what an override IS — and **a request handler was not callable**, because handlers carry `symbol_kind: "endpoint"` and `_type_indices` indexed only `"method"`. Re-run on the audited commit: **affected 254 · unknown 14 · unaffected 0 of 268**, with every one of the 7 rows the audit proved reachable now `affected`. Batteries `tests/test_endpoint_disposition_p03.py` (9 assertions) |
|
|
56
|
+
| B15-res | **The `.partial` is not removed on the error path.** B15 closed the atomic-temporary half in 5.8.1 and it holds — the null device answers `exit 0` with no residue, the OS locale is isolated in `os_error`, the hint names the parent-directory decision — but a fourth artefact was outside the `finally`: `ask spring-audit . -o ./saint-server` against an existing directory leaves `?? saint-server.partial` in the caller's repository. The auditor has cleaned the user's repository by hand in four consecutive sessions. | 5.8.0 → 5.8.2 re-audits (residual of `B15`) | Medium — small in code, large in claim: **the pitch is read-only deterministic analysis**, and a pre-CI security review blocks a tool that writes into the client's tree and leaves it there | **closed 5.8.3** (`6fd8001`) — registration is about OWNERSHIP, not about which writer did the writing, and the `register_artifact` call sat inside the branch of `PhasedRun._write` that writes the file itself. The three commands that supply `_safe_write_file` therefore wrote a checkpoint nobody owned, so the failure path ran `cleanup_artifacts()` against an empty registry. Reproduced before and after on `spring-audit -o <existing dir>`: `cleaned_up: []` + residue → `cleaned_up: ["target-dir.partial"]` + a clean tree. Battery `tests/test_hygiene_rows_b15res_b18_ask16.py`, with the negative control that a delivered answer leaves no checkpoint and cleanup does not fire on a good run |
|
|
57
|
+
| B16 / ASK-09 | *(see the `ASK-09 / B16-sv` row above — same defect, reopened there.)* The 5.8.2 saint-server audit files it as `B16` and the corpus audit as `ASK-09`; both describe the ceiling hint denying a `--compact` that exists and fits. Recorded here only so a reader searching `B16` lands on the reopened row rather than concluding it is untracked. | 5.8.2 re-audits | High | **closed 5.8.4 — tracked as `ASK-09 / B16-sv`**, which closed by measurement on `tutorials` with a battery that overflows for real |
|
|
58
|
+
| B18 | **`--min-band` rejects the tier vocabulary that our own report teaches.** `audit-report` prints *"Risk tiers: R1 14, R2 7, R3 74, R4 3"*, the crosswalk *"R1 = band critical …"*, and the instruction *"Filter tiers with --min-band"* — and then `ask risk . --min-band R1` answers `INVALID_INPUT` and lists the severity spellings (`critical`, `high`, `medium`, `low`) as the only ones it takes. Two surfaces of one product, one teaching a vocabulary the other refuses. | 5.8.1 → 5.8.2 re-audits | Medium — **a regression introduced by the `B2` fix**: B2 gave the band a tier spelling precisely so it could not be read as a severity, and the filter was never taught the new vocabulary, so the product documents a filter expression it refuses | **closed 5.8.3** (`6fd8001`) — `risk_model.band_of` resolves either spelling from the crosswalk the report is already rendered from, with `BAND_OF_TIER` derived rather than retyped so the two cannot drift. `--min-band` and `--band` on `risk`, and `--min-band` on `enrich`, all read it; a rejection names both spellings. Measured on BroadleafCommerce: `--min-band R1` and `--min-band critical` both return 0 rows, `--min-band R2` returns 18. The second thread is closed too — `TIER_CROSSWALK_NOTE` names the command the flag lives on (`ask risk --min-band`) and what the other two carry instead. ⚠ **This row read `open` for its whole first day closed**: the verdict was written into the *defect* cell because the quoted error message carried escaped pipes (`\|`), and a reader — or a `grep` — counting open rows counted this one. The quoted message no longer carries a pipe; `tests/test_ledger_rows_are_wellformed.py` now asserts that every row has exactly five cells and that no cell but the last opens with a verdict. |
|
|
59
|
+
| B19 | **The schema registry resolves 44 identifiers and one of them has a document.** Residual of `B6`, which the same audit calls excellent and better than what it asked for: every identifier resolves, `pending_renames` declares the 13 non-conforming ones with their canonical name, and `endpoints` emits `endpoints-v1`. But `with_published_document: 1` — only `envelope-v1` has a JSON Schema behind it, and the rest resolve to their *description*. | 5.8.2 re-audit (residual of `B6`) | Medium — resolution enables **discovery**; the use case that opened B6 was **validation in CI** (`--ci` / `--fail-on`), and that needs the document, not the description | **closed 5.8.4 for the five a gate consumes** — `verify-v1`, `verify-edit-v1`, `endpoints-v1`, `pr-impact-v1` and `core-analysis-v1`, the set the audit puts at 90 % of CI use, are published as JSON Schema documents and served by `ask schema <name>`; `with_published_document` goes **1 → 7** (six identifiers; `pr-impact` and the root analysis each carry two emitted values pointing at one document). **The documents are held to the payloads, not to prose**: every one is validated in the battery against output the suite produces, so a document that stops describing what the command emits fails — a schema written from a reading of the code drifts the first time the code moves. Cross-checking against a second repository is what made them true rather than fitted: `spring-petclinic` caught `verify-edit`'s verdict vocabulary, which is `pass` / **`break`** / `unverified` and not the `fail` the first draft assumed — `break` is a statement about the change, not about the repository. **The two canonically-named documents sit beside emitted values that are still bare numbers** (`1.3`/`1.0` for `pr-impact`, `1.0` for the root analysis), deliberately: renaming an emitted `schema_version` is the incompatible change `ASK-11` exists to stop us making in silence, so `ask schema 1.0` still resolves to all seven shapes and each offers its canonical name to pin. Asserted both ways — every claimed document exists, every document is claimed, and no emitted identifier was quietly renamed to match one. Battery `tests/test_published_schema_documents_b19.py`, 30 assertions. ⚠ **Still open**: the other ~37 identifiers, which continue to resolve to their description. Original note — the row is honest about this already (`documented_note` says exactly which of the two it gives), so this is a scope completion rather than a correction. Recommended order, from the audit: the five a gate would actually consume — `verify-v1`, `verify-edit-v1`, the root analysis (`1.0` → `core-analysis-v1`), `endpoints-v1`, `pr-impact` (`1.2` → `pr-impact-v1`) — which it estimates at 90 % of CI use. Generate from the code that already owns each shape rather than by hand, since `entries[].constant` already names the authority. Publish `with_published_document` as a progress metric. |
|
|
60
|
+
| ASK-16 | **74 % of `spring-audit`'s wall clock is outside the instrumented phase.** Clean CPU, all layers warm, writing to a file to bypass ceiling estimation: wall 9,4 s against `analysis_time_ms: 2 454` — 26 % of the clock. `symbols_analyzed` identical across 5.8.0/5.8.1/5.8.2 (30 053), output grew 8–10 % while time doubled, and stdout-vs-file is only ~1,6 s, so serialisation, population, cache state and ceiling estimation are all **excluded as causes by measurement**. ~5,4 s unattributed. The command recovered 7 of the 10,4 s it lost in 5.8.1 and sits **+45 % over its 5.7.2 best** (7,6 s → 11,0 s). | 5.8.1 → 5.8.2 corpus re-audits | High | **closed 5.8.3 by measurement** (`6fd8001`) — `perf.PhaseTimings` is always on and always published: `metadata.timings` gives the per-phase split, the measured total and `unaccounted_ms`, published rather than distributed over the phases. **The missing 74 % is the CIR build.** BroadleafCommerce, 2 985 files: cold wall 9 690 ms — `cir_build` 8 679 (90 %), `security_rules` 732, `semantic_model` 63, `tx_rules` 53, unaccounted 163 (1,7 %); warm wall 2 133 ms — `cir_build` 1 118, `security_rules` 735, unaccounted 164 (7,7 %). So `analysis_time_ms: 2 454` was never the analysis, it was the security rule pass alone, and the optimisation target is the parse, not the rules. The auditor's ordering — instrument before tuning — was right, and it changed the answer |
|
|
61
|
+
| R2 | **WebFlux functional routing is not modelled.** Sixth consecutive cycle. `ask endpoints halo` returns `total: 0` over a CMS with a full REST API: `functional_routing: {files: 58, route_registrations: 183, modeled: false}`, `gaps: ["webflux_functional: 58 files"]`. Independently counted in the repository: 3 `@RestController` (all three are tests) against 89 files using `RouterFunctions`/`coRouter`/`SpringdocRouteBuilder`. | 5.6.1 → 5.8.2, every cycle | Medium — **the signalling is exemplary and carries no misreading risk**: the zero is declared with its cause, its count and a prose warning naming the failure mode. What is missing is the analysis, not the honesty | **open (6 cycles)** — cascades: `posture`, `risk`, `data-exposure` and `spring-audit` all derive their population from `cir.endpoints`, so a repository at 0 endpoints makes four commands report an empty surface honestly and uselessly (`spring-audit halo` = 0 findings on a full REST CMS). Acceptance: `endpoints halo` returns a non-empty inventory agreeing with Halo's published API on a 20-route sample, `supported_styles.webflux_functional: true`, and path resolution composes the builder chain (`route().path("/apis/x").GET("/{name}", …)`). **The only open row that requires new analysis, and the one both audits name as the single item separating the product from a 9,5.** |
|
|
62
|
+
| C3-127 | **`validation` is the only command with sustained monotonic degradation.** Steady-state, three consecutive versions worsening: 17 268 → 18 273 → 21 337 ms, **+23,6 % over the record**, while every other command returned to record level or better after the cache converged. `ASK_PROGRESS=1` attributes 100 % of it to one phase, `mapping validation surface`. | 5.8.2 re-audit | Medium | **open, premise corrected by measurement (5.8.3, `e57f3e7`).** `risk` publishes `timings` now, and on BroadleafCommerce the two substrates this row is about are **2,6 s of 13,5 s (19 %)** — `indexing the validation surface` 2 103 ms, `resolving the conditional bean graph` 525 ms — while **75 % of the clock is `reading HTTP-input query sinks` (10 051 ms)**, a phase the row does not mention. Two consequences: the ~27 s figure is a property of the audited corpus, not of the command; and within a single `risk` invocation each substrate already runs exactly once, so the saving this row imagines is a **cross-command** cache — a feature with its own contract, not a patch. What stays open is the real one: the query-sink walk |
|
|
63
|
+
| P1-proc | **A performance regression battery still does not exist, and the sixth request now arrives with a validated protocol.** `R6` (the parse cache never converging, so timings were irreproducible) was a real defect, was fixed well in 5.8.2 — dispersion between consecutive warm runs fell from 2,81× to a median of 1,04×, and `cache status` no longer prints its `over by N MB` line — and **was found only because an auditor happened to be measuring**. The same auditor then reported a 43 % regression that did not exist, and retracted it: a 2-run protocol produces artefacts up to **2,8×**. | 5.5.6 → 5.8.2, sixth request | Medium — process, not code | **open** — 5.8.2 shipped the *assertions* (`P1 / F-BR`: absolute ceilings and `max(sample) < 8 × posture_warm`, whose validation table the audit independently reproduces and extends with 5.8.2 at 6,8× ✅). What is still missing is the **harness that runs them**, and the audit supplies the missing half — a measurement protocol this ledger should treat as binding: `wall_steady()` = one discarded warm-up, then **4 runs**, report the **minimum**, and fail the measurement itself when `max/min ≥ 1,25` because at that point the cache is still evicting and no number is comparable. **Two independent corpora now report the same confounder**: 5 of 7 false positives in one audit and 4 of 7 in the other came from cache state or CPU contention — a competing `ask` process took `spring-audit` from 11,0 s to 71,4 s (+549 %), and `analysis_time_ms` inherits the bias (2 454 → 8 109 ms), so it is not an independent metric either. Acceptance: the battery runs `wall_steady`, asserts the reproducibility gate first, and no performance figure enters this ledger without a clean-CPU check. ⚠ **The cheap half closed 5.8.4, and the cause was not `endpoints`**: it was instrumented all along — one `Progress()`, started and finished — but a phase was announced by the *heartbeat*, which only fires once the interval has elapsed, so a run that finished inside 5 s printed nothing and an instrumented fast command was indistinguishable from an uninstrumented one. The counts the audit reported (posture 1 · spring-audit 2 · validation 3 · risk 5) were measuring how slow each command was, not how well it reports. Entering a phase is now announced whether or not the interval has passed, at `start()` and at every `update()`; the `_last_emit` stamp is still taken there, so the timed loop waits a full interval behind it and the two cannot double-print, and a counted stage is still rate-limited (asserted). `endpoints` also names the snapshot write as its own phase rather than charging that time to the scan. Measured: `endpoints` **0 → 2** phase lines on a 3-file repository, and on `tutorials` the first line arrives at `elapsed=0.0s` instead of after five seconds of silence. Battery `tests/test_phase_boundaries_are_announced_p1proc.py`, 8 assertions. |
|
|
53
64
|
|
|
54
65
|
---
|
|
55
66
|
|
|
@@ -82,7 +93,7 @@ The facts these rows are keyed to are published: `ask schema facts-v1` prints th
|
|
|
82
93
|
| C1-23 | **`risk` publishes findings under the defect label `spring-audit` deduplicates.** Field: `total_defects: 24` while SEC-004 for one class is ranked three times (lines 27, 1285, 1290) and one TX-006 twice, so `by_band.critical: 4` is two defects. Measured on BroadleafCommerce: `spring-audit` `total_findings: 20` / `total_defects: 15`, `risk` `total_defects: 20` over **15 distinct `defect_id`** — six `(rule, symbol)` pairs ranked twice | 4.1.0-class (eval #9) | **High** — `risk` is the composition layer shipped in 4.0.0 to make severity trustworthy, and it inherited the counting defect the release before it removed (C1-5, C2-14) | **closed 4.2.0** — `build_risk` iterates `defect_identity.group_by_defect`, the same grouping `spring-audit` publishes beside it, so one row is one thing to fix and both commands move together (asserted directly: the two counts are compared in one test rather than pinned to a literal). The observations are not dropped — `witness_count` and `witness_sites` carry where each was seen, because the remedy is at the symbol and several witnesses otherwise print the same line and read as duplicates (C2-16's rule one level up) — and the census gets its own name, `total_findings`, beside a `counts_note`. `rule_id` becomes **`rule_ids`**: one defect can be witnessed by TX-001 and TX-006 at once, and a singular field would have to pick one and lie about the other (`risk` is `experimental`, which is the tier that permits this). `severity` is the strongest across the witnesses, with `factors.defect_severity.basis` saying so. Measured on BroadleafCommerce: **20 rows → 15, exactly `spring-audit`'s 15 defects / 20 findings**. Original note: the authority exists and is not bound: `defect_identity` already produces the `(category, defect_kind, symbol)` identity `spring-audit` reports, and `risk` carries `defect_id` per row while counting rows. Fix is the C1-5 remedy: rank **defects**, carry the witnesses under each (the call sites are what the reader triages by, per C2-16), and if an observation count is worth publishing it gets its own name — never `total_defects` |
|
|
83
94
|
|
|
84
95
|
| C1-24 | **`verify --init` derives a contract asserting an invariant the same build reports as violated.** The derivation proposes `forbid_finding` for every audit pattern that fires nowhere today, and computes "fires nowhere" from `verify_rules._audit_findings` — `run_tx_audit` + `run_security_audit` over the CIR. **SEC-004…SEC-007 are not produced there.** They come from `security_config_scan`, a scan over source and descriptor text that `spring-audit` runs *as well*, and that this derivation does not. So a repository storing passwords under a fast unsalted digest gets `spring-audit` → `SEC-004 high`, and `verify --init` → `no-sec-004: "SEC-004 fires nowhere in this repository"`, on the same tree, in the same process class. Committed without `--dry-run`, the gate is red on the first run against the state it was derived from. **Second half of the same root cause**: SEC-005, SEC-006 and SEC-007 appear in **neither** list — not proposed, not rejected — because the catalogue is `_DEFAULT_SECURITY_PATTERNS`, which stops at SEC-004. A rule can therefore be silently outside contract derivation and nothing says so | 4.5.1-class (eval #10) | **Critical** — the contradiction is between two commands of one engine over one state, which is the determinism premise the product is sold on; and it is on `verify`, the `core`-tier command whose promise is *"safe to gate CI on"*. Scored the CI-reliability axis **4/10** | **closed 4.5.3** — `_audit_findings` takes the `root` and threads it to both audits, so the derivation, the gate (`verify`), the diff gate (`verify-edit`) and the self-check all evaluate the *same* audit; the catalogue is now read from `rule_catalog.RULES`, the authority `--help` and the README already render from, so a rule joins contract derivation by existing. Measured on the reproduction fixture: SEC-004 moves from **proposed as `no-sec-004`** to **rejected with its reason** (*"1 finding(s) of SEC-004 exist today — that is debt for the baseline"*), and SEC-005/006/007 appear for the first time. **The self-check had the same hole**: `execute_candidates` evaluated without a root too, so the net built to stop exactly this rule approved it — a safety net blind in the same way as the thing it guards is not a second opinion, and both halves are asserted, including a pinned negative control that fails if the root is ever dropped again |
|
|
85
|
-
| C1-25 | **Reachability is measured at type scope and attributed at member scope.** `risk` sets `reach = "http" if endpoints else "internal"` from `run_impact_chain(symbol)`, where the symbol is the declaring **type**. Both directions are wrong and both were measured in the field: a defect in a widely-used utility type inherits every endpoint that reaches the type (reported: 3 412 of 3 574 endpoints for a member with **2** real call sites, ~1 700× over-attribution, carrying a `critical 16.93` score on a `reach=http` the member does not have); and a repository-wide control defect declared in a `@Configuration` class scores `reach=internal`, because that class is not itself HTTP-reachable, when its real scope is the whole endpoint surface. **The ranking is inverted exactly where it matters most** | 4.5.1-class (eval #10) | **High** — it is the ordering of `risk`, the composition layer, and it degrades global security configuration to `low`. Blocks the `exposure` join (F-C): publishing that table on this model would put a ~1 700× over-attribution behind a CISO-facing headline | **open** — needs member-level reach plus a declared-scope rule for configuration-scoped defects; feature-sized, not a patch. **Second field witness, eval #11 (4.5.3):** the member half shipped in 4.5.3 and the evaluator confirms it (*"reach por método"*); the configuration half reproduces unchanged, and the complaint is now about the **sentence**, which is the part that travels — *"`risk` degrada `@Configuration` a `low` con el enunciado 'no HTTP endpoint in this repository reaches it'. No es una banda conservadora: es falso en efecto"* — **configuration half closed 4.6.0, unverified in the field (eval #12).** Reach for a repository-wide control defect is now **declared, not walked**: `posture` publishes `chains.decides_endpoints`, `risk` reads it as the reachability value `request_chain`, and the two units stay apart (`endpoints_reached` is the walk, `decides_requests` is the scope). Evidence-bound both ways: only a file declaring readable chain rules qualifies, and an unresolved servlet path clears the attribution. Fixture: **low 2.8 → medium 4.48**. The field could not confirm it — `risk` costs ~45 min there (C3-42) |
|
|
96
|
+
| C1-25 | **Reachability is measured at type scope and attributed at member scope.** `risk` sets `reach = "http" if endpoints else "internal"` from `run_impact_chain(symbol)`, where the symbol is the declaring **type**. Both directions are wrong and both were measured in the field: a defect in a widely-used utility type inherits every endpoint that reaches the type (reported: 3 412 of 3 574 endpoints for a member with **2** real call sites, ~1 700× over-attribution, carrying a `critical 16.93` score on a `reach=http` the member does not have); and a repository-wide control defect declared in a `@Configuration` class scores `reach=internal`, because that class is not itself HTTP-reachable, when its real scope is the whole endpoint surface. **The ranking is inverted exactly where it matters most** | 4.5.1-class (eval #10) | **High** — it is the ordering of `risk`, the composition layer, and it degrades global security configuration to `low`. Blocks the `exposure` join (F-C): publishing that table on this model would put a ~1 700× over-attribution behind a CISO-facing headline | **open** — needs member-level reach plus a declared-scope rule for configuration-scoped defects; feature-sized, not a patch. **Second field witness, eval #11 (4.5.3):** the member half shipped in 4.5.3 and the evaluator confirms it (*"reach por método"*); the configuration half reproduces unchanged, and the complaint is now about the **sentence**, which is the part that travels — *"`risk` degrada `@Configuration` a `low` con el enunciado 'no HTTP endpoint in this repository reaches it'. No es una banda conservadora: es falso en efecto"* — **configuration half closed 4.6.0, unverified in the field (eval #12).** Reach for a repository-wide control defect is now **declared, not walked**: `posture` publishes `chains.decides_endpoints`, `risk` reads it as the reachability value `request_chain`, and the two units stay apart (`endpoints_reached` is the walk, `decides_requests` is the scope). Evidence-bound both ways: only a file declaring readable chain rules qualifies, and an unresolved servlet path clears the attribution. Fixture: **low 2.8 → medium 4.48**. The field could not confirm it — `risk` costs ~45 min there (C3-42) ⚠ **Field verification, 2026-08-17 (Broadleaf @ `8645873661` + openmrs-core).** **Member half: confirmed.** `risk` composes 22 defects on Broadleaf with `reach_scope: method` on 21 and `class` on the one class-level defect; `endpoints_reached` is 0/10/26/27/30 against a 3 574-endpoint repository — no type-scope inheritance anywhere, and `endpoints_reached_basis` names the wider question. openmrs-core: 37 of 37 method-scoped. **Configuration half: still unconfirmed, and not for lack of trying** — it needs a repository-wide control defect declared in a file that decides the request chain, and no repository in the local corpus produces one (Broadleaf's 22 are 21 × TX-001 plus a cookie flag; openmrs-core's 37 are all `internal`; keycloak-config-cli, spring-boot-admin, sagan and alfresco-community-repo compose 0 defects). The row stays open on that half alone. Against C3-42's ~45 min in the field, `risk` now costs **16,9 s** on Broadleaf. ⚠ P0-2's first cut reopened this row from the other side and was caught by this verification: `ReachExpansion.extra_keys` treated both halves alike, so a query about `Base#m` admitted the callers of every member of every subtype. The subtype half now narrows to the corresponding member; the DI bridge deliberately does not, because an injection site names the type and there is no member-level injection edge to find (`a6cbe21`). |
|
|
86
97
|
| C1-26 | **`risk` does not read the other authority that answered its own access axis.** `RiskComposer` asks `posture` **only** when `--profile` is given (`if self.profiles is not None`); without it the axis is `endpoint_security_surface` alone, and an endpoint that surface leaves at `coverage_unknown` scores `auth_verdict: unknown` (×1.4) even when the request-chain resolution for the repository **as configured** decides it open (×2.0). Measured in the field on a defect whose reached endpoint carries `security_policy: none_detected` in the same payload: banded `high 8.06` where the decided verdict bands it `critical`. The composition layer is the one command whose entire product is reading the authorities beside it | 4.5.3 (eval #11) | **High** — it understates the severity of exactly the class the product is bought for, and it is the evaluator's first mandatory item. It also makes the default invocation weaker than the flagged one, which is backwards: `--profile` should *refine* an answer, not be the price of getting one | **closed 4.6.0 — shipped, but unverified in the field.** The resolution is read always; with no set named it is the one Spring performs with none given (`default` alone, the permissive case per CL-8), and every row publishes `profile_set`, `profile_set_basis` and `profile_decisions`. Fixture measurement: **8.06 → 11.52**, exactly the ratio of the two access weights, every other factor unmoved. **Eval #12 could not re-measure it in the field: `risk` costs ~45 min on that repository (C3-42), so the evaluator abandoned it.** The row stays honest about that — this fix is behind the performance row, and it is one of the two composition items the field wanted most |
|
|
87
98
|
| C1-27 | **Two definitions of "the working tree changed" inside one command.** `cache freshness` reports STALE with `RIS HEAD == current HEAD` and `Delta: 0` because **one untracked file no analyser reads** (an editor settings file) is a porcelain line. C3-40 (4.5.3) replaced a false *fresh* with a false *stale*: its predicate is *any* `git status` entry, while the fact the snapshot describes is *the files the analysis admits*. Reproduced on a battery repository with a clean tree: `FRESH` → write `.claude/settings.json` → `STALE` | 4.5.3 (eval #11) | Medium — a freshness signal that fires on a file the analysis cannot see teaches a reader to ignore the one that matters, and it invalidates a warm cache for nothing on a repository where a cold run costs minutes | **closed 4.6.0 — verified in the field (eval #12).** One authority decides which paths qualify (`path_filters.analysis_reads`) and both consumers ask it: the freshness boolean and the **tree signature**, which is also the cache key — so an agent writing its own settings file was discarding every warm answer. `.github` stays a change (tooling evidence); the default is inclusive, because a missed change is a false *fresh*. Field: `FRESH` / `Uncommitted: False` with the same untracked settings file still present |
|
|
88
99
|
| C1-28 | **The test/production split still has two authorities.** C1-2 made `test_sources` the authority in 3.2.1 and rebound five consumers; `path_filters.is_test_path` was never retired and still answers for its own callers. They disagree on five path shapes, in both directions — `src/main/java/**/Test*.java` (`False` / **`True`**), `src/main/java/**/*IT.java` (**`True`** / `False`), `src/main/java/**/test/Helper.java` (`False` / **`True`**). Field symptom: `--compact` counts **1 test file that lives in `src/main`**, with the verdict (0.0 %, critical) correct and the numerator wrong | 4.5.3 (eval #11) | Medium — it is the denominator of every coverage statement, and the direction that inflates is the one a reader must never be misled about (C1-2's own rule) | **closed 4.6.0 — verified in the field (eval #12).** `path_filters.is_test_path` is now a passthrough to the authority, after absorbing everything it knew that the authority did not, and a declared **main** source root outranks a naming convention. Fleet effect, cold: keycloak 5 525 → 5 486 Java files with endpoints **676 → 678**, openmrs-core 866 → 861, Broadleaf / alfresco / petclinic unchanged. Field: `0 test files for 3337` **with the reason stated** (no declared test source root, no file matching a test naming convention) — the numerator and its basis, which is what made it verifiable from outside |
|
|
@@ -379,11 +390,11 @@ class's subject, not its provenance.
|
|
|
379
390
|
|---|---|---|---|---|
|
|
380
391
|
| E-1 | **Prose opened a declaration, and the file left the graph.** The declaration joiner decided whether a line *starts* a type declaration by scanning raw text. A comment carrying `class`/`interface`/`enum`/`record` followed by an identifier — `/** Implements the interface only for the legacy path. */`, or a trailing `// starter class is not an utility class` on an annotation — matched, so the join swallowed the annotation and the real declaration into one entry beginning with `@`, which the main loop reads as a pending annotation and never examines. The file then yields **zero symbols**: no type, no endpoint, no transaction boundary, nothing | 3.7.0 (battery, while building the C3-13 fixture) | Medium | **closed 3.7.0**. Comments are stripped before the keyword scan, block state carried across lines and string-literal aware; the join's terminating `{` must also be code, so a commented brace no longer cuts a real multi-line declaration in half. Measured A/B over six repositories (petclinic, eureka, open-banking-gateway, openmrs-core, Broadleaf, keycloak): **open-banking-gateway recovers 6 types — three of them Spring Boot application classes** — and **no repository loses a single symbol** |
|
|
381
392
|
| E-2 | **The security configuration scan cannot tell code from a comment.** `security_config_scan._scan_java` and `_scan_descriptor` match on raw text with no comment model. Reproduced at unit scope: `SEC-004` is returned identically for a live statement, a `//` comment and a `/* */` block; `SEC-007` identically for a live and a commented-out XML block. **Measured cost in the field: 4 of 13 `high` findings were false — 31 %** — and the damage is worse than the count, because the correlation engine wrote a persuasive paragraph over a commented-out block (*"these deployments share one credential, so the least protected decides the strength of all"*). **Confident prose about dead code is worse than a terse warning: it invites trust.** The same blindness runs in reverse and costs a *finding*: a transport-guarantee constraint that is commented out is invisible, so a mitigation somebody switched off is never reported, and it aggravates a cookie finding reported beside it. E-1 fixed exactly this class for the declaration joiner in 3.7.0; the configuration scan was written afterwards without the lesson | 4.5.1-class (eval #10) | **High** — it is the precision axis (scored 5,5/10) and the reason the evaluator concludes the output *"no es publicable sin verificación humana"* | **closed 4.5.3** — one authority, `source_text`, wired at the **three points a file is read** rather than inside each rule, so a rule added later is covered by construction instead of by whoever remembers. Comments are **blanked, not deleted**: offsets and line count are preserved, so every observation still points at the line a reader can open (asserted). Two properties are asserted as hard as the fix, because getting either wrong trades one wrong answer for another — **string literals survive** (`hibernate_strat._strip_comments_strings` blanks literal *content* on purpose, and these rules carry their evidence *inside* the literal: `MessageDigestPasswordEncoder("SHA-1")`, `<param-value>admin:{SHA-256}…`), and a `//` or `/*` **inside** a string is not a comment (`"http://example.com"`). A/B over the battery: BroadleafCommerce, openmrs-core, keycloak and spring-petclinic report byte-identical security findings — the fix removes false positives and loses no true one. `source_text.commented_spans` is published unused, so the *reverse* half — a control that is present but switched off — has a seam to be reported from rather than a second parser (DEAD-001, queued as F-A) |
|
|
382
|
-
| E-3 | **A handler that inherits its `@RequestMapping` loses the authorization annotation it declares, and 54,7 % of a monolith's HTTP surface is published as unguarded.** The inheritance projection in `_build_route_surface` indexes a projected route under the **declaring** member — the abstract method on the superclass that owns the mapping — and every security lookup downstream reads that symbol. The gate lives on the `@Override` in the concrete controller, which carries no mapping annotation and is therefore never consulted. Census on the subject (3 574 endpoints, parser validated 15/15 against hand-checked cases): **1 955 handlers inherit their mapping, carry a live gate in source, and are published `none_detected`**; 909 that declare their own mapping and carry the same gate are published `custom_gate_inferred`; 363 inherit and genuinely carry nothing; 115 declare and carry nothing; 111 carry a gate that is commented out (correctly `none_detected`, DEAD-001). The shape is the generic CRUD base class — one abstract class with the mappings and no security, 235 controllers extending it. Runtime semantics confirm the direction: the aspect's pointcut is `@Before("@annotation(…)")`, which matches the executing method — the override — so the annotation **is** applied and only the reading stops at the declaration. **Propagates to eight commands**: `endpoints`, `spring-audit`, `posture`, `data-exposure`, `risk`, `impact`, `enrich`, `audit-report`, plus `retrieve endpoint-security
|
|
393
|
+
| E-3 | **A handler that inherits its `@RequestMapping` loses the authorization annotation it declares, and 54,7 % of a monolith's HTTP surface is published as unguarded.** The inheritance projection in `_build_route_surface` indexes a projected route under the **declaring** member — the abstract method on the superclass that owns the mapping — and every security lookup downstream reads that symbol. The gate lives on the `@Override` in the concrete controller, which carries no mapping annotation and is therefore never consulted. Census on the subject (3 574 endpoints, parser validated 15/15 against hand-checked cases): **1 955 handlers inherit their mapping, carry a live gate in source, and are published `none_detected`**; 909 that declare their own mapping and carry the same gate are published `custom_gate_inferred`; 363 inherit and genuinely carry nothing; 115 declare and carry nothing; 111 carry a gate that is commented out (correctly `none_detected`, DEAD-001). The shape is the generic CRUD base class — one abstract class with the mappings and no security, 235 controllers extending it. Runtime semantics confirm the direction: the aspect's pointcut is `@Before("@annotation(…)")`, which matches the executing method — the override — so the annotation **is** applied and only the reading stops at the declaration. **Propagates to eight commands**: `endpoints`, `spring-audit`, `posture`, `data-exposure`, `risk`, `impact`, `enrich`, `audit-report`, plus `retrieve endpoint-security\|security-surface`. It also retires this repository's own headline: *"2 635 endpoints with no security policy (73,7 %)"* is really **≈659** | 5.3.1 (audit #28) — **the first E row found in the field** | **Critical** — the largest single mis-classification this ledger has recorded, running in the unsafe direction, on the axis the product is bought for, in the answer the field quotes most | **closed 5.4.0** — the projection now names the member that runs. A projected route carries `effective_symbol` (the override where one exists, the declaration otherwise) beside `symbol`; the phase-3 security projection reads a gate the override declares for itself **method-level only**, so the fix can add a policy and never widen one away; and both `custom_gate_inferred` carry sites — the assembled route surface every CIR consumer reads, and the `endpoints` extractor — read the annotation set of **both** members of the pair. Second half, same root and found while fixing it: `_infer_gate_annotations` classified an inherited-mapping override as a non-handler, which puts the only members carrying the gate on the wrong side of the specificity ratio — diluting it at this subject's scale and **suppressing the gate outright** on a repository where most controllers extend a generic base. A member the route surface publishes as a route is a handler. 7 regression assertions over both surfaces and both vocabularies (a repository-declared gate and `@PreAuthorize`), **5 of 7 red on the previous build**; the two that are green are the negative controls — an ungated override stays ungated, because a fix in this direction must not invent a policy either. Suite 8 149 |
|
|
383
394
|
| E-4 | **E-3's residue: the annotation-to-method pairing is decided on raw text, so a blank line switches a security verdict off.** 24 endpoints in 14 controllers still publish `none_detected` with a live gate, out of the 1 955 E-3 corrected, and they partition perfectly into two lexical triggers. **(a) 18 endpoints** — a blank line between the annotation and the declaration (`@Override`, `@M3FiltroSeguridad(...)`, *blank*, `public List<MutuaDto> obtenerTodos()`): **0/18** detected, against 1 931/1 937 = 99,7 % where no blank line intervenes. `repository_ir` clears `pending_anns` in the fall-through at the end of the per-line loop, and the only blank-line guard sits **inside** the block-comment branch, so a genuinely empty line reaches the reset and empties the buffer. **(b) 6 endpoints** — a space between the type and its generic argument (`public List <CategoriaProfesionalDto> obtenerTodos()`): **0/6**. The `return_type` group is `[\w.<>\[\]?,]+`, which admits no space, so `List` matches, `\s+` eats the space, and `(?P<name>[a-z_]\w*)` is offered `<Categoria…>` and fails — **the whole declaration fails to match and the method is dropped**, which is the exact loss the regex's own comment three lines above warns about for modifier-position annotations. Java applies the annotation identically in both forms. In every one of the 14 controllers only `obtenerTodos` fails while its siblings (`crear`, `borrar`, `actualizar`, `obtenerPorId`) resolve. **E-3's 7 regression assertions cover neither form** | 5.4.0 (audit #29) | **Medium** — small population, unsafe direction, and it shows the pairing is still lexical rather than structural | **closed 5.4.1** — two edits, one per trigger. A blank line is skipped before the fall-through that clears the pending-annotation buffer: Java binds an annotation to the next declaration whatever whitespace separates them, and nothing about an empty line is a statement the reader must forget. The `return_type` group admits a space before the generic argument, so `List <Dto>` matches and the method exists again with its endpoint, its gate and its return-type edge. The spelling is normalised where it enters `stable_id` — `List <Dto>` and `List<Dto>` are one type and may not become two identities — and only the space *before* a bracket is removed, so `Map<String, List<X>>` keeps the id it already had. Four assertions added to E-3's battery, all four red on the previous build's source: both forms on the assembled route surface, both on the `endpoints` extractor, and the identity. ⚠ The class of defect is not closed by this row: the pairing is still lexical, which is what F-BP is queued for |
|
|
384
395
|
| E-5 | **A route without its own recorded `source` defaults into the Spring MVC bucket, so a JAX-RS-only repository is reported `spring_detected: true`.** `repository_ir.py:6693`: `"source": route.get("source", "annotation")`. `repository_ir.py:7126`: `coverage.detected_styles.spring_mvc_annotations` sums every route whose `source == "annotation"`. JAX-RS routes are identified separately (`framework == "jax_rs"`, line 7131) but are never given their own `source` key, so they fall into the same default and are counted in **both** buckets. Reproduced on four independent repositories: neo4j (0 `org.springframework` imports anywhere in the tree, `spring_detected: true` published anyway, golden-repo battery #1) · killbill (282 pure-JAX-RS endpoints, all double-counted, battery #2) · examples (`OrdersService`'s JAX-RS routes counted into `spring_mvc_annotations: 3` alongside `jax_rs: 3`, battery #1) · tutorials (`spring_mvc_annotations: 2275` equals the entire `by_source.annotation` population, `jax_rs: 172` counted a second time on top of it; sum of `detected_styles` exceeds `total` by 236 — direct proof of overlap, battery #2). `migrate-check` classifies the same repositories correctly (`spring_present: false` on neo4j and killbill), so the correct classification signal already exists in the codebase and is not shared with the endpoint-source default | 5.7.0 (golden-repo battery #1 2026-08-14, #2 2026-08-15) | **High** — a `core`-tier command (`endpoints`) and the `spring_detected` field `spring-audit` also publishes both assert a confident falsehood on a repository with zero real Spring usage, reproduced on 4 of 16 repositories tested | **closed 5.7.1** — the annotation family is recorded where the route is built, at **both** construction sites. Phase 2 already computed it for `framework` and now writes `source` from the same condition, so the two fields cannot disagree. Phase 3 — the inheritance projection — was the half the triage missed: it is not Spring-only, and it builds its own dict. neo4j's four `/tx` routes are declared by an abstract **JAX-RS** resource (`AbstractCypherResource`) and projected onto `CypherResource`; with no family recorded they fell to the same default, which is why fixing phase 2 alone left neo4j reading `spring_mvc_annotations: 4` on a repository with zero Spring. The family is read from the declaring symbol's annotations rather than from phase 2, because an abstract parent emits no route of its own and `routes` has no entry to answer from. Third edit, same row: `detected_styles.jax_rs` was the one style counted over the **raw route surface** while its three siblings counted the published census — the actual source of *"styles sum past `total`"*, which was two denominators, not only double counting. It now counts the census, and `by_source["jax_rs"]` is the same number. `_SEMANTIC_MODEL_ENDPOINT_SOURCES` gains `jax_rs`: these handlers **are** analysed by `spring-audit`, so splitting them out of the `annotation` bucket may not shrink `semantic_model_endpoints` — the parity assertion is carried as a negative control. Measured: neo4j `spring_mvc_annotations` 4 → **0** (10/10 jax_rs) · killbill 282 → **0** (282 jax_rs, styles sum 564 → 282 = total) · keycloak 695 → **0** (695 jax_rs) · examples 3 → **0**; spring-petclinic (17) and BroadleafCommerce (268/4/1) byte-identical. 6 assertions, **4 red on the previous build**, the two green ones being the negative controls. Suite 8 351 (21 previous reds unchanged). **Re-measured on the full golden set before the bump** (23 repositories × `endpoints`/`spring-audit`/`posture`, A/B against `3ebd2b6`, `--no-cache`, `ASK_READONLY=1`): **62 of 69 pairs byte-identical**, 7 moved and every one in this direction, 0 status or exit changes. The battery found a **fifth** repository the row did not name — eureka, 18 endpoints `annotation` → `jax_rs`, `spring_mvc_annotations` 18 → 0. It also moved one number that is **not** a correction to a route: jobrunr's `detected_styles.jax_rs` 3 → 0, because its three JAX-RS routes belong to `org.jobrunr.quarkus.it.JobRunrFunctionalityResource` in the integration-test module and never entered the census. Verified not to be a loss: the payload publishes them in `test_util_endpoints` with `scope: "test_util"` and `test_util_excluded: 3`, each now carrying the `source: "jax_rs"` this row added. Same shape on keycloak (764 raw routes → 695 census). ⚠ **The row's own headline is half wrong and that half is NOT closed here**: `spring_detected` is not computed from `source`. Its one authority is `spring_model.spring_detected` — a bean container or a declared transaction boundary — so neo4j still publishes `spring_detected: true` after this fix. That half is **E-12**: bean detection matches the annotation's simple name, and neo4j declares its own `@Service` |
|
|
385
396
|
| E-6 | **A module-name heuristic meant for `test-framework`-shaped modules excludes a real, deployed Spring Boot surface, and the top-level fact is published with no gap flag.** `repository_ir.py:6363-6382` (`is_test_source_file`): a file under `.../src/main/...` is correctly not a test by the file-level authority (`test_sources.is_test_path`), but the function separately excludes it when the enclosing module's path prefix has any segment `p.lower().startswith("test")` (line 6375) — written for modules like `test-framework`/`test-providers`/`testsuite`, which the docstring says "have no product of their own." Apache SkyWalking's own e2e harness is rooted at a top-level directory literally named `test/` (`test/e2e-v2/java-test-service/*/src/main/java/...`), which matches the rule on the first path segment even though the files under it are real `@RestController`/`@GetMapping` classes (`HealthController`, `AlarmController`, `LogController`) — not test infrastructure with no product. Effect: `ask endpoints` on the full repository returns `total: 0`, `spring_mvc_annotations: 0`; `ask spring-audit` returns `spring_detected: false`, for a repository with confirmed (narrow, 23-file) real Spring usage. `--include-tests` recovers all 20 routes, and running `ask endpoints` directly on the submodule finds them without any flag — proving detection works once the module-exclusion is bypassed. `coverage.gaps` stays empty on the default run; the exclusion note (`test_source_excluded_detail`, "29 route annotation(s) from test source trees excluded") reads as routine test filtering, not as "an entire production-shaped module was treated as test infrastructure" | 5.7.0 (golden-repo battery #2 2026-08-15) | Medium — a judgment-call heuristic operating exactly as designed for its intended target, overreaching on a repository whose harness happens to be named `test/`, but landing on the same headline field (`spring_detected`) E-5 does, with no hedge published | **closed 5.7.2 — the disclosure half, and the admission left standing on purpose.** Whether an e2e harness rooted at `test/` is product or infrastructure is a judgement, and overturning it on one repository would move surface on twenty-three; what was indefensible was publishing it as routine test filtering. `test_source_excluded_detail.excluded_by_module_name_rule` now names the modules, counts the route annotations, states that the rule judges the **module** rather than the file, and gives both ways back (`--include-tests`, or run the command on the module path); `coverage.gaps` carries the entry, because that list is what a reader checks before trusting a zero. Only the module-name branch is reported — `is_test_path` never fires inside a declared main source root, so it cannot remove a deployable module. Measured: skywalking names 3 modules and 29 route annotations where it named none; spring-petclinic (17) and jobrunr (12) gain nothing. ⚠ Residue, deliberate: `spring-audit`'s `spring_detected: false` still carries no hedge on this repository — the exclusion fact does not reach that surface, and wiring it there is its own row |
|
|
386
|
-
| E-7 | **A security-policy classifier regex matches a method's own declaration text, not a call site, and the resulting false flag is cached per-file and leaks onto a handler explicitly marked bypass-all.** On sa-token (a non-Spring-Security Java auth framework with its own `@SaCheckLogin`/`@SaCheckPermission`/`@SaIgnore` annotations), `ask endpoints` labels `AtCheckController`'s handlers `policy: "programmatic"` although no `sourcecode.config.json` custom-security declaration exists (`ask config` confirms "Declaration: none"). The only evidence found is a regex matching the literal text `checkPermission(` in the file — which matches the demo's own method **declaration**, `public SaResult checkPermission()`, not a call to a security check. Reported at `repository_ir.py:394-408`/`:5064-5076` (field-agent-located; not independently re-read this session, one level less verified than E-5/E-6/C1-47/C1-48). The flag, once set, is applied to every handler in the file lacking its own recognized policy — reaching `ignore()`, a handler annotated `@SaIgnore` (sa-token's explicit "skip all checks" marker), which is reported as `programmatic`-protected, the opposite of what the annotation on it says. Contamination reproduces in `impact-chain`'s `endpoints_affected` for the same controller | 5.7.0 (golden-repo battery #2 2026-08-15) | **High** — this is an auth-verdict false positive on the exact axis `ask risk`'s own documentation says its composition depends on (`severity_effective = ... × auth_verdict × ...`); a wrongly-`programmatic` handler would suppress severity on a genuinely open endpoint | **closed 5.7.1** — **re-verified at source first, as this row required**, and the field report was exact. `_PROGRAMMATIC_SECURITY_RE` matched `\b(
|
|
397
|
+
| E-7 | **A security-policy classifier regex matches a method's own declaration text, not a call site, and the resulting false flag is cached per-file and leaks onto a handler explicitly marked bypass-all.** On sa-token (a non-Spring-Security Java auth framework with its own `@SaCheckLogin`/`@SaCheckPermission`/`@SaIgnore` annotations), `ask endpoints` labels `AtCheckController`'s handlers `policy: "programmatic"` although no `sourcecode.config.json` custom-security declaration exists (`ask config` confirms "Declaration: none"). The only evidence found is a regex matching the literal text `checkPermission(` in the file — which matches the demo's own method **declaration**, `public SaResult checkPermission()`, not a call to a security check. Reported at `repository_ir.py:394-408`/`:5064-5076` (field-agent-located; not independently re-read this session, one level less verified than E-5/E-6/C1-47/C1-48). The flag, once set, is applied to every handler in the file lacking its own recognized policy — reaching `ignore()`, a handler annotated `@SaIgnore` (sa-token's explicit "skip all checks" marker), which is reported as `programmatic`-protected, the opposite of what the annotation on it says. Contamination reproduces in `impact-chain`'s `endpoints_affected` for the same controller | 5.7.0 (golden-repo battery #2 2026-08-15) | **High** — this is an auth-verdict false positive on the exact axis `ask risk`'s own documentation says its composition depends on (`severity_effective = ... × auth_verdict × ...`); a wrongly-`programmatic` handler would suppress severity on a genuinely open endpoint | **closed 5.7.1** — **re-verified at source first, as this row required**, and the field report was exact. `_PROGRAMMATIC_SECURITY_RE` matched `\b(?:…\|checkPermission\|…)\s*\(`, which is the shape of a *call* and equally the shape of the *method that declares one*. On `AtCheckController` the regex finds **one** match in the entire file — line 46, `public SaResult checkPermission() {` — and on that evidence all **seven** handlers were published `policy: programmatic`, `ignore()` among them, which carries `@SaIgnore`, sa-token's explicit skip-all-checks marker: the verdict stated the opposite of the annotation on the method. Repo-wide the false population was **23 endpoints (programmatic 41 → 18)**, and every one of the 15 surviving files was re-read and carries a real call site (`StpUtil.checkPermission("")`, `stpLogic.hasRole(role)`, `SaRouter.match(…)`). The discriminator is what precedes the name on its own line: a call is led by a receiver, an operator, a bracket, a statement boundary or one of `return`/`new`/`throw`/…; a declaration is led by its return type — an identifier, a closing generic, or a closing array bracket. ⚠ **The first attempt introduced a false negative and the cross-repository A/B is what caught it**, not the suite: applied to the whole pattern the filter also ran on the receiver alternatives, and `ReactiveSecurityContextHolder.getContext()` reads as identifier-then-match — exactly what a declaration looks like — so spring-security-samples' reactive `MeController` silently lost a correct verdict (that repository ships **two** `example.MeController` classes, which is how it surfaced). The pattern is now split: the ambiguous `name(` half is filtered, the unambiguous receiver/member/`throw` half never is, and that negative control is the sixth assertion in the battery. **Measured across all 23 golden repositories: 22 of 23 policy censuses byte-identical**, sa-token the only mover — BroadleafCommerce (12), keycloak (147) and spring-security-samples (3) unchanged. 6 assertions, **3 red on the previous build**, the 3 green ones being the negative controls. Suite 8 761. ⚠ Residue this row does not close: sa-token's handlers now read `none_detected`, which is honest but incomplete — `@SaCheckPermission` is a vendor annotation this analyzer models only when declared in `sourcecode.config.json`, and that is the documented custom-security path, not a new gap |
|
|
387
398
|
| E-8 | **`impact-chain` silently merges unrelated same-named classes from different modules into one answer; `impact` on the identical symbol correctly refuses.** tutorials (Baeldung's mega-repo, hundreds of independent modules) has 4 distinct classes named `PersonService` in unrelated modules. `ask impact PersonService <repo>` returns `resolution: "ambiguous"` and lists all 4 `matched_fqns`. `ask impact-chain PersonService <repo>` returns `resolution: "partial"`, but its `direct_callers` mix classes from two different, unrelated modules (`com.baeldung.activej.*` and `com.baeldung.hibernatejfr.*`) into one merged answer and a single risk score (25.0, `critical`), with no field disclosing which of the 4 classes contributed which caller. The command that should have said "ambiguous" said "partial" and guessed | 5.7.0 (golden-repo battery #2 2026-08-15) | Medium-High — only demonstrated on a repository large and diverse enough to carry real name collisions across modules (tutorials, 24 074 java files — the largest repository this battery has run against); a confident composite risk score built from an admittedly-ambiguous symbol resolution is the opposite of `impact`'s own behavior on the same input | **closed 5.7.2** — on C1-41's precedent rather than a second one of its own. The facts stay (every caller listed is a real caller of *some* candidate, and dropping them would hide reach the reader asked about); the figure that reads as *this symbol's risk* is void — `risk_score` and `risk_score_raw` null, `risk_level` `unknown` — with the reason published beside the null in `risk_score_basis`. The candidates are named in `metadata.matched_classes` / `matched_classes_count`, and both a warning and the explanation say so, because an agent quotes the explanation and a script reads the metadata. `impact-chain` is `core` tier, so no published enum value is invented: `resolution` stays `partial` and `risk_level` uses `unknown`, which its schema already documents, and every new key appears only on an ambiguous match — an unambiguous answer is byte-identical. Measured on tutorials: 4 candidates named where the payload named none, `high`/25.0 replaced by `unknown`/null, the six direct callers still listed. Regression: `tests/test_ambiguous_chain_e8.py`, 8 assertions |
|
|
388
399
|
| E-9 | **An unresolved JAX-RS path's caveat is dropped between `endpoints` and `impact-chain`, and the path's embedded regex syntax collides with the `endpoint_id` field separator.** killbill's `AccountResource` declares `@Path("/{accountId:" + UUID_PATTERN + "}")` (string-concatenated). `ask endpoints` correctly marks it `path_resolution: "unresolved"`. `ask impact-chain AccountResource <repo>` shows the same route without that caveat and truncates the path to `"/{accountId:"`, and because the embedded `:` collides with the `endpoint_id` separator the emitted id is malformed: `DELETE:/{accountId::org.killbill.billing.jaxrs.resources.AccountResource:...#closeAccount` | 5.7.0 (golden-repo battery #2 2026-08-15) | Low | open |
|
|
389
400
|
| E-10 | **Malformed Java is silently absorbed into the symbol export with full confidence, and the parse failure is not named anywhere in the payload.** spaghetti-api's `BrokenSyntax.java` has 2 open braces and 0 close braces — invalid Java, confirmed by brace count and by reading the file. The root command's `contracts` export nonetheless reports it with a clean `class BrokenSyntax` export plus a `method` export (signature `()->void`), no `parse_error`, absent from `analysis_gaps` — and it never surfaces in `spring-audit`, `migrate-check` or `risk` output either, no "N files failed to parse" anywhere in any of them | 5.7.0 (golden-repo battery #1 2026-08-14) | Medium — a file that cannot be Java is exported as if it parsed cleanly, on the exact axis this repository's own CLAUDE.md is most explicit about ("never a confident falsehood... unknown, never 0") | **closed 5.7.3** — the recovery stays (a file that fails to compile still tells us its type exists); what changes is that it is qualified. A `parse_structure` gap names every file whose braces do not balance **once comments and strings are masked**, and states what is missing from the model: the members after the unclosed block and the type's own end. Distinct from `parse_coverage`, which fires on *total* extraction absence — this one fires when extraction succeeded over source that cannot be complete, and both readings of "this file was not fully read" now live in `reconciliation`. Cost is a `str.count` per file; the mask, which is not cheap, runs only for the rare file that already looks unbalanced, so a brace inside a string or a comment is not a false positive (asserted). Measured on spaghetti-api: `BrokenSyntax.java` named, its symbols retained. ⚠ Residue: the gap reaches every CIR consumer through `analysis_gaps`, but the root command builds its own list from `ConfidenceAnalyzer`, so `ask <repo> --compact` still does not carry it. Regression: `tests/test_parse_structure_gap_e10.py`, 7 assertions |
|
|
@@ -412,6 +423,7 @@ class's subject, not its provenance.
|
|
|
412
423
|
| E-33 | **`verify-edit`'s contract diff attributes a removed method to the wrong type and misses a `throws` clause removal that breaks callers.** On a real working-tree change it reported `SpringBootBootstrappingServletContextListener#run` as a removed public method — `run()` belonged to an **anonymous inner class** — and did not report that `protected createInitializer(...)` lost `throws PrivilegedActionException`, which breaks every caller that catches it. The verdict itself was right (`break`, `contract_broken: 1 public API change`, correct `head_sha`, `security_delta: false`) | 5.7.3 (field, BroadleafCommerce, D8) | Low — the gate answered correctly; the evidence beside the answer is what is wrong, and this gate's product is its evidence | **closed 5.8.0** — both halves are about what an owner and a signature *are*. **Owner:** the extractor now opens a scope on an anonymous class body (`new Runnable() {`) and names it the way Java does, `Outer$1`, so the members inside are attributed to it and the type itself is a symbol — a member's owner always exists in the model. Both public-surface readers (`verify_edit._nodes_by_fqn`, `contract_diff.extract_public_contract`) skip owners containing `$`: nothing outside can name an anonymous type, so nothing can depend on it, and the discriminator is the owner rather than a naming rule. A creation whose argument list spans lines is not matched — the scope stays with the enclosing type, as before, rather than being guessed. **Signature:** the `throws` clause is captured and published as `(String)->Object throws PrivilegedActionException`, which is DR-1 applied to the other half of the declaration. End-to-end on the field's own shape: `verify-edit` went from *2 public API changes* (one of them the phantom `#run`) to **1**, and that one is the `throws` removal it used to miss entirely, cited from and to. Fleet A/B: symbol population BroadleafCommerce 30 427 → 30 694, killbill 18 513 → 18 887, spring-petclinic 267 → 269, mall 15 752 → 15 757 — the growth is the anonymous types themselves; their members are **re-attributed, not added** — and the endpoint population is byte-identical on all five (268 / 285 / 17 / 240 / 0). Regression `tests/test_contract_evidence_e33.py`, 9 assertions covering both halves and the negative controls (the named type keeps its own members; a method that throws nothing says nothing) |
|
|
413
424
|
| E-34 | **A type that declares parameters of its own has no supertype anywhere in the model — not an edge, not a signature.** Two independent readers of the `extends`/`implements` clause, both written as if a declaration went straight from the type's name to its supertype. **(a)** The `extends`/`implements` **edge** builder (`repository_ir.py:3229-3232`) matches `(?:class\|interface)\s+(\w+)(?:\s+extends\s+…)?(?:\s+implements\s+…)?\s*\{`: after the name it requires whitespace, so `class Generic<T, V> extends Base {` matches **nothing at all** — not a partial match, no edge of either kind. **(b)** `SymbolRecord.signature` drops the clause the same way, so `class B<T, V> extends HashMap<String, V>` is recorded as `class B`, and `extends_map` — derived from that signature at `repository_ir.py:7043` and handed to `_build_route_surface` — inherits the hole. Minimal end-to-end witness: three classes, `Plain extends Base` and `Generic<T, V> extends Base`; `ask explain Base` publishes **`Used By: Plain`** and nothing else. Census over 8 repositories, counting only types whose declaration really does carry a clause: **254 of 8 182 (3,1 %)** lose it — open-banking-gateway 57 of 595 (**9,6 %**), keycloak 144 of 4 356, killbill 25 of 838, BroadleafCommerce 26 of 1 965 | 5.7.3 (found while closing E-29(c), 2026-08-16) | **High** — it is silent, it is repo-wide, and it removes the one relation the two commercial blockers are about to be fixed by. A generic abstract controller loses its inheritance projection (the E-3 family), a generic filter loses the supertype `container_wiring` classifies on (E-28), and any supertype **closure** built for E-27 will walk a chain with holes in it and report the result as complete | **closed 5.8.0** — one root, one fix: the type's own parameter block is now matched as a **balanced** `<…>` span instead of "everything up to the next `>`". The unbalanced form stopped inside `<T, V>` and, to reach the body brace, had to swallow the whole `extends …` clause — which is why the same defect appeared twice from one cause, in `_CLASS_DECL_RE` (the signature) and in the edge builder (`repository_ir.py:3229`, where it matched nothing at all). Balancing is also what settles the bounded-parameter trap without a second rule: the `extends` inside `class E<T extends Map<String, X>>` belongs to the parameter, and the reader now walks past it to the type's own clause. Nesting is bounded at four levels, past which the old unbalanced branch is kept as a **fallback**: it loses the clause as it always did, and the type still matches — a declaration this product cannot fully read must not vanish from the model. ⚠ The signature half was weighed as the row asked and carries no churn: `_compute_stable_id` is built from `(package, class, kind, name)` and never from `signature`, and `contract_diff.extract_public_contract` reads signatures of **method** nodes only, so restoring a type's clause changes no identity and no contract diff. Two consumers that read the clause off `signature` (interface closure `:3688`, `Api`-interface controllers `:6737`) gain the generic types they were silently skipping. Measured A/B over **25 repositories** (61 k `.java`): **0 type declarations lost, 0 gained** — the population is byte-identical — and clause-carrying declarations recovered wherever the shape occurs: dubbo +207, alfresco +130, jenkins +104, keycloak +74, langchain4j +67, BroadleafCommerce +26, killbill +27; keycloak-config-cli and mall +0, which is the correct answer for repositories without the shape. Regression `tests/test_generic_supertype_clause_e34.py`, 15 assertions: the end-to-end witness (`explain Base` now names `Plain`, `Generic` **and** `Bounded`), the `extends` edge in the CIR, the signature, and the negative controls — record components, enums, an unparameterised type, and the past-depth declaration that must still match with no clause. The E-29(c) test that documented this hole from the other side was updated to assert both readers agree |
|
|
414
425
|
| E-35 | **A statement is parsed as a method declaration, and the method it invents is published with a call edge pointing at it.** `return unknownHelper("x");` matches the declaration shape read by `_METHOD_DECL_RE` — no modifiers, "return type" `return`, name `unknownHelper`, parameter list `"x"` — so the symbol table gained `com.a.BasicCtl#unknownHelper` with `signature: ("x")->return`, an `in_degree` of 1 and a `calls` edge from the real method that contains the statement. Census over 5 repositories: **BroadleafCommerce 1 047 of 23 913 methods (4,4 %)**, killbill 846, keycloak-config-cli 76, mall 11, spring-petclinic 6 | 5.7.3 (found while writing the E-27 negative controls, 2026-08-16) | Medium — it inflates every method population and every degree derived from it, and a phantom callee is the shape a reader cannot check: the symbol does not exist to be looked up | **closed 5.8.0** — the discriminator is the return-type slot, because nothing declares a method whose type is `return`. `_STATEMENT_HEADS` lists the keywords that start a statement and can stand where a type is read (`return`, `throw`, `new`, `else`, `assert`, `yield`, `case`, `do`, `instanceof`, `break`, `continue`, `catch`, `finally`); **`void` is deliberately absent — it is a return type**. Measured A/B: phantoms **1 047 → 0** on Broadleaf, 846 → 0 on killbill, 76 → 0 on keycloak-config-cli, and the method population falls by **exactly** that count on each repository, so no real declaration was lost; the endpoint population is byte-identical on all five. Regression `tests/test_statement_is_not_a_declaration_e35.py`, 10 assertions, of which 7 are negative controls — every real declaration in the same file, `void` kept as a type, and a method that returns a constructed object ⚠ **Testigo de campo, 2026-08-17: este cierre movió un invariante publicado y no lo declaró.** La re-auditoría de `saint-server` mide `metadata.symbols_analyzed` cayendo de 43 642 a 43 003 (−639, −1,46 %) tras cinco versiones constante, la clasifica P0 y la nombra *"AS-14 otra vez"*. Es exactamente esta fila: los dos invariantes que lo confirman —`endpoints_analyzed` 3 742 y `total_findings` 385— están intactos en su propio informe, y el −1,46 % cae dentro del censo de fantasmas medido aquí. La corrección es correcta; lo que falta es `metadata.symbols_excluded` con su razón, y la regla que AS-14 ya fijó: una población analizada no cambia entre releases sin un campo que lo explique. Se sigue como **B17-sv** en el roadmap. |
|
|
426
|
+
| E-36 | **A declaration that shares its line with its annotations is not a declaration.** `@GetMapping("/orders") public List<Order> orders() { … }` is ordinary Java, and the parse loop read it as an *annotation line*: it registered `@GetMapping`, moved on, and never parsed the method. Two losses, both silent. The handler left the symbol table, so the route lost the member that serves it; and the mapping stayed pending and bound to the **next** declaration in the class, so the route was published on a method that does not serve it — reproduced on a constructed controller as `GET /api/compact -> notAHandler`, with the real handler absent. A controller written entirely in this style answers `total: 0` under a `zero_result_reason` saying no routing construct was found. | 5.8.3, found while re-measuring `ASK-09` — the battery's own fixture writes its handlers this way and had therefore been asserting over a **0-endpoint** payload since it was written | **High** — the mis-attribution half is a wrong answer rather than a missing one, and every command downstream of the route surface keys on the handler symbol: `impact` computes a blast radius for the wrong member, `spring-audit` reads the wrong method's gate, `risk` composes over both | **closed 5.8.4** — annotations are peeled off the front of the line by `_split_leading_annotations`, and what remains is re-read as the declaration it is. One authority for where an annotation ends (`_scan_annotation`), shared with `_parse_annotation_line`, because a run of annotations can only be walked safely if both agree on the boundary. Measured on `tutorials` (24 073 `.java`): **2 263 → 2 268 endpoints**, five recovered, none lost, each verified at source — four handlers and one `@ResponseBody public FileSystemResource getFile(…)`. The other ten golden repositories are byte-identical, which is the point: the census cannot see this until a repository written in the compact style arrives. ⚠ **Two negative controls, one of which was the first cut's own regression**: a trailing comment is not a declaration (`@SaCheckPermission(…) // 注解式鉴权…` fell through to the loop's pending-annotation reset and cost sa-token a route, 214 → 213, caught by the corpus A/B and not by the suite); and only a **method or type** declaration is taken from a shared line — a field is left where it was, because `@Autowired private X x;` would add a DI `injects` edge and the interface bridge does not narrow to a member (`C1-25`, open), so the recall would arrive as a false caller rather than a better answer. Battery `tests/test_annotation_shares_its_line_e36.py`, 17 assertions |
|
|
415
427
|
|
|
416
428
|
## Class P — Product and procurement (not analysis defects)
|
|
417
429
|
|