IPipelineValidator exposes both Validate(IServiceProvider) and ValidateAsync(IServiceProvider); ArtifactContractValidator.Validate implements the sync member by blocking on the genuinely async module-discovery path (GetRunnableModulesForArtifactValidationAsync(...).GetAwaiter().GetResult()), which duplicates the async implementation line-for-line and risks deadlocks if validation is ever invoked under a synchronization context. PipelineValidationService only ever calls ValidateAsync, so the sync member is dead weight that every validator must still implement.
For v4: drop the sync Validate from the interface (or give it a default implementation delegating to ValidateAsync), aligning with the codebase's otherwise consistent async-suffix convention.
Evidence: src/ModularPipelines/Validation/ArtifactContractValidator.cs:20-32, src/ModularPipelines/Validation/IPipelineValidator.cs, src/ModularPipelines/Validation/PipelineValidationService.cs:15-26
Raised by the v4 scoping audit round 3 — umbrella #3239.
IPipelineValidatorexposes bothValidate(IServiceProvider)andValidateAsync(IServiceProvider);ArtifactContractValidator.Validateimplements the sync member by blocking on the genuinely async module-discovery path (GetRunnableModulesForArtifactValidationAsync(...).GetAwaiter().GetResult()), which duplicates the async implementation line-for-line and risks deadlocks if validation is ever invoked under a synchronization context.PipelineValidationServiceonly ever callsValidateAsync, so the sync member is dead weight that every validator must still implement.For v4: drop the sync
Validatefrom the interface (or give it a default implementation delegating toValidateAsync), aligning with the codebase's otherwise consistent async-suffix convention.Evidence:
src/ModularPipelines/Validation/ArtifactContractValidator.cs:20-32,src/ModularPipelines/Validation/IPipelineValidator.cs,src/ModularPipelines/Validation/PipelineValidationService.cs:15-26Raised by the v4 scoping audit round 3 — umbrella #3239.