Plan Checker Extensions - V18 First Look
- mschmidt33
- Jan 6
- 7 min read
Introduction
With the release of Eclipse Version 18, Varian introduced a new plan checking framework that brings structure, transparency, and extensibility to pre-approval plan review. In the previous post, Plan Checker Plugins – V18 First Look, we focused on understanding that framework at a high level: what the Plan Checker is, how it fits into the clinical workflow, and how developers can build custom Plan Checker plugins using the Eclipse Script Wizard.
That first installment walked through the practical mechanics of creating a Plan Checker plugin, highlighting the role of the different configuration types—AutomaticCheckerConfiguration, InformationCheckerConfiguration, ManualCheckerConfiguration, and RegexCheckerConfiguration—and how they can be combined to build institution-specific plan review logic while reusing the native Plan Checker user interface and reporting tools. It also clarified an important architectural distinction: Plan Checker plugins act as the host that assembles and runs plan checks, while Plan Checker extensions exist to define new automatic check logic that plugins can consume.

In this post, we shift our focus to that next layer: Plan Checker extensions. Rather than assembling checks from existing building blocks, extensions allow developers to implement entirely new automated plan checks by inheriting method behaviors and class objects from CheckBase. This opens the door to clinic-specific logic and tolerances that go beyond Varian’s built-in automation types and enables deeper customization of plan evaluation tests and behavior.
The Plan Checker Extension is a customizable class library which can then be added to custom Plan Checker Plugins to define new QA tests outside the built-in configurations or duplicate the behavior of AutomaticCheckerConfiguration check types with different tolerances.
If you haven’t read Plan Checker Plugins – V18 First Look, please consider starting there to get familiar with the Plan Checker architecture and configuration patterns before diving into extensions. With that foundation in place, this post will explore what Plan Checker extensions are, how they differ from plugins, and what a first implementation looks like in practice. The code in this post has been pushed to the online Dose Check Extensions repository on Github.
The Plan Checker Extension
The Plan Checker Extension is a .NET Framework Class Library extension. The result of which compiles into a dynamic-linked library which can then be added to custom Plan Checker Plugins to define new QA tests outside the built-in configurations, or perhaps duplicating the behavior of AutomaticCheckerConfiguration check types with different tolerances.

It is most straightforward to start a Plan Checker Extension using the Eclipse Script Wizard. Within the Eclipse Script Wizard select the option Plan Checker Extension. In this example, the extension has been named DoseCheckExtensions.
The generated class inherits from the CheckerBase class. Properties from these classes include the plan types that this checker extension should be admitted to check, StagePlanTypes, a Description property, Name property, and more. Additionally, a RunChecks method is overriden in the main script file where the custom code is added for additional checks.
Within the RunChecks method, there are two variables added: baseCheckResults and checkResults. baseCheckResults check that the incoming context is not null which also, and the checkResults variable is the initial list on which the developer can begin to build their checks. The final return of this method is the CollectResults method which returns the results of the CheckResult collection to the plugin.
namespace DoseCheckExtensions
{
[CheckerClass("18c28598-b477-4d63-87ad-f2558e6760f5")]
public class DoseCheckExtensions : CheckerBase
{
public DoseCheckExtensions(CheckerPlanTypes stagePlanType, string name, IReadOnlyList<string> description, string checksPassedMessage)
: base(stagePlanType, name, description, checksPassedMessage)
{
// Empty
}
public override IReadOnlyList<CheckResult> RunChecks(ICheckerContext context)
{
var baseCheckResults = base.RunChecks(context);
var checkResults = new List<CheckResult>();
// TODO : Add here the code that is executed when Plan Checker script launches.
return CollectResults(baseCheckResults, checkResults);
}
}
}In the following example, a plan checker extension script will be generated that performs an automatic check -- a check with either a success or failure -- on the heterogeneity correction selection and the location of the maximum dose, and a user interacted check -- a question the user can verify as being correct-- regarding the overlapping structures with the maximum doses. To summarize the plan for the upcoming extension.
Dose Extension Checks
Heterogeneity Correction is turned on.
Success: Heterogeneity Correction is turned on.
Error: Heterogeneity correction is not on.
Maximum Dose Location
Success: Max dose is located inside the target.
Error: No target volume defined.
Error: Maximum dose location not in target.
Structures overlapping with max dose
Question: Report structures overlapping with the max dose.
Custom Plan Check Development
The first check should be simple to check. Within the calculation algorithm settings there is a HeterogeneityCorrection which can either be set to "ON" or "OFF". In the code snippet below, the first treatment field is extracted using the First() linq extension. The field should also contain an "X" in the energy mode denoting a photon beam. The second if statement checks the HeterogeneityCorrection tag from the PhotonCalculationOptions of the plan. If the value is turned on, a CheckResult of Success is added to the checkResults variable, and an Error is added otherwise.
var plan = context.Plan;
// TODO : Add here the code that is executed when Plan Checker script launches.
//Check if the heterogeneity correction is turned on.
if (plan.Beams.First(b => !b.IsSetupField).EnergyModeDisplayName.ToUpper().Contains("X"))
{
if (plan.PhotonCalculationOptions["HeterogeneityCorrection"].ToString().ToUpper() != "ON")
{
checkResults.Add(CheckResult.Success("Heterogeneity correction is turned on for photon beams."));
}
else
{
checkResults.Add(CheckResult.Error("Heterogeneity correction is not turned on."));
}
}The next code snippet checks to ensure the maximum dose of the plan is inside the target. First, the plan's TargetVolumeID property is validated, adding an Error to the checkResults if null. Otherwise, the structure is selected based on the match with the TargetVolumeId property and the IsPointInsideSegment method is used to check if the max dose location is within the target. On a positive return of this method, a Success is added to the checkResults.
//additional checks to ensure the dose max is inside the target.
if (String.IsNullOrEmpty(plan.TargetVolumeID))
{
//Check first that target volume is defined.
checkResults.Add(CheckResult.Error("No target volume defined in the plan."));
}
else
{
//Check that the dose max is inside the target volume.
var target = plan.StructureSet.Structures.FirstOrDefault(st => st.Id.Equals(plan.TargetVolumeID));
var maxDosePoint = target.IsPointInsideSegment(plan.Dose.DoseMax3DLocation);
if (maxDosePoint)
{
checkResults.Add(CheckResult.Success($"The maximum dose {plan.Dose.DoseMax3D} is inside the target {target.Id}."));
}
else
{
checkResults.Add(CheckResult.Error($"The maximum dose {plan.Dose.DoseMax3D} is outside the target volume."));
}
}Finally, the last code-snippet reports all structures overlapping with that max dose point. The expectation is for the user to validate whether the overlapping structures are appropriate for the given plan. To determine these structures all structures are iterated and the same IsPointInsideSegment method is evaluated with the location of the maximum dose as an input argument. The Question check result type takes an additional argument for the approve message. Finally, the CollectResults method is returned to the calling Plan Checker Extension.
//Report all structures that overlap the target volume.
List<string> structuresOverlappingMaxDose = new List<string>();
foreach(var structure in plan.StructureSet.Structures)
{
if (structure.IsPointInsideSegment(plan.Dose.DoseMax3DLocation))
{
structuresOverlappingMaxDose.Add(structure.Id);
}
}
//First parameter in question not shown in the UI.
checkResults.Add(CheckResult.Question("Structures overlapping max dose are appropriate.",
$"Structures Overlapping Max Dose: {String.Join(", ", structuresOverlappingMaxDose)}"
));
return CollectResults(baseCheckResults, checkResults);Integration with Plan Check Extensions
The dose check extensions resultant from the code snippets above will be added to the plan checker plugin detailed in the prior blog post and available in the Gateway Plan Checker repository on Github. To add these custom checks to the plan checker plugin:
Compile the class library in Visual Studio noting the location of the compiled dll file.
In the GatewayPlanChecker application, right mouse click on the References in the solution explorer, click Browse, and browse to the DoseCheckExtensions.dll that was recently compiled.
Reference the custom dose checks as an AutomaticCheckerConfiguration as noted below.
Find the section of the code just before the doseStage tab is generated.

Add the following code to include the custom dose checks. These will be stored in a check section named "Custom dose Checks" with a description listed as the final input parameter.
//Add custom Dose Checks
doseChecks.Add(new AutomaticCheckerConfiguration(typeof(DoseCheckExtensions.DoseCheckExtensions),
"Custom Dose Checks",
new List<string> { "Checks on calculation algorithm settings",
"Checks on max dose location"},
"Custom dose checks completed."));Compile the plan checker extension script. Within Eclipse, run the plan checker extension as any binary plugin can be run. Running this shows the Custom Dose Checks at the bottom of the dose tab.

An Error check in this instance, with heterogeneity turned off shows an immediate warning available to the physicist checking the plan. The Question check is less urgent, but allows for the user to check the result.

Additionally, the tooltip shows the description provided within the AutomaticCheckerConfiguration generated in the plan checker plugin -- which is in turn passed into the extension script.

Summary
Eclipse Version 18 introduces a fundamentally new approach to plan review by separating how plan checks are orchestrated from how plan checks are implemented. Across the last two posts in this series, we explored both sides of that architecture. In Plan Checker Plugins – V18 First Look, we demonstrated how custom Plan Checker plugins act as the host layer—assembling checks, organizing review stages, and presenting results through the native Plan Checker UI using configuration-based building blocks such as AutomaticCheckerConfiguration, InformationCheckerConfiguration, ManualCheckerConfiguration, and RegexCheckerConfiguration. In this post, we completed that picture by introducing Plan Checker extensions, which enable developers to define entirely new automated QA logic by inheriting from CheckBase and implementing custom clinical rules that go beyond Varian’s built-in checks and fixed tolerances.
Together, these features along with the new Script approval extension scripts, which allow for these automatic validations to be intertwined with the plan approval process, represent a major shift in how plan quality and safety logic can be embedded directly into the planning workflow. This allows developers to more easily encode institutional standards, clinical judgment, and physics expertise directly into Eclipse—whether through structured documentation, user-validated questions, configurable automation, or fully custom algorithmic checks without focusing on the development and maintenance of user interfaces and application infrastructure. These tools form a cohesive V18 framework for transparent, enforceable, and extensible plan quality assurance, positioning ESAPI as not just a scripting interface, but a first-class platform for clinical safety innovation.






Comments