Problem Statement
Custom step module authors currently have no clean way to register the mapping from their step type name to their handler function. The step registry (Step.Type → handler function name) is hardcoded in IdLE.Core for built-in steps. For external/custom steps, the only available path is passing a Providers.StepRegistry hashtable at every Invoke-IdlePlan call:
Invoke-IdlePlan $plan -Providers @{
StepRegistry = @{
'MeineOrg.Step.SAP.CreateUser' = 'MeineOrg.IdLE.Steps.SAP\Invoke-SAPCreateUser'
}
}
This is unintuitive and leaks an implementation detail (the handler function name) into caller code. Every consumer of the plan must know and repeat this mapping.
The root cause is two separate data structures that belong together:
| Structure |
Location |
Contains |
StepMetadataCatalog.psd1 |
Per step module |
Step contract (capabilities, With schema) |
| Step Registry |
Get-IdleStepRegistry.ps1 in Core |
Step.Type → handler function name |
The catalog is already loaded automatically during module discovery (IdLE.Steps.* + Get-IdleStepMetadataCatalog). The registry is not.
Proposed Solution
Add an optional Handler key to the StepMetadataCatalog.psd1 format:
# MeineOrg.IdLE.Steps.SAP/StepMetadataCatalog.psd1
@{
'MeineOrg.Step.SAP.CreateUser' = @{
Handler = 'MeineOrg.IdLE.Steps.SAP\Invoke-SAPCreateUser' # <-- new
RequiredCapabilities = @('sap:identity:create')
WithSchema = @{
RequiredKeys = @('SapSystemId')
OptionalKeys = @('TargetOU')
}
}
}
During Resolve-IdleStepMetadataCatalog, when a step pack catalog is loaded, any Handler entries are automatically fed into the step registry — no extra wiring by the caller required.
Module-qualified names (ModuleName\FunctionName) should be encouraged so the handler function does not need to be globally exported, avoiding session-scope pollution. Resolve-IdleStepHandler already supports this lookup path via Get-Module -All.
Alternatives Considered
- Keep
Providers.StepRegistry as the only mechanism: Works but requires callers to repeat implementation details. Not suitable as the primary extensibility path.
- Separate
StepRegistry.psd1 file per module: Cleaner than the parameter workaround, but unnecessary indirection when the catalog already exists and is already loaded.
Impact
Handler is optional — built-in steps whose handlers are hardcoded in the registry do not need it. Fully backwards compatible.
- Custom step module authors declare everything in one place (
StepMetadataCatalog.psd1).
- Callers never need to touch
Providers.StepRegistry for well-behaved step modules.
- Handler functions can remain private/nested — no session-scope pollution.
Assert-IdleNoScriptBlock guard must be extended to cover Handler values (must be a plain string, not a ScriptBlock).
- Conflict handling needs a decision: if a step type appears in both the hardcoded registry and a catalog
Handler entry, which wins?
Additional Context
Relevant files:
src/IdLE.Core/Private/Resolve-IdleStepMetadataCatalog.ps1 — catalog loading, lines 265–358
src/IdLE.Core/Private/Get-IdleStepRegistry.ps1 — hardcoded registry, lines 110–221
src/IdLE.Core/Private/Resolve-IdleStepHandler.ps1 — two-tier handler resolution
src/IdLE.Steps.Common/StepMetadataCatalog.psd1 — reference catalog format
Problem Statement
Custom step module authors currently have no clean way to register the mapping from their step type name to their handler function. The step registry (Step.Type → handler function name) is hardcoded in
IdLE.Corefor built-in steps. For external/custom steps, the only available path is passing aProviders.StepRegistryhashtable at everyInvoke-IdlePlancall:This is unintuitive and leaks an implementation detail (the handler function name) into caller code. Every consumer of the plan must know and repeat this mapping.
The root cause is two separate data structures that belong together:
StepMetadataCatalog.psd1Withschema)Get-IdleStepRegistry.ps1in CoreThe catalog is already loaded automatically during module discovery (
IdLE.Steps.*+Get-IdleStepMetadataCatalog). The registry is not.Proposed Solution
Add an optional
Handlerkey to theStepMetadataCatalog.psd1format:During
Resolve-IdleStepMetadataCatalog, when a step pack catalog is loaded, anyHandlerentries are automatically fed into the step registry — no extra wiring by the caller required.Module-qualified names (
ModuleName\FunctionName) should be encouraged so the handler function does not need to be globally exported, avoiding session-scope pollution.Resolve-IdleStepHandleralready supports this lookup path viaGet-Module -All.Alternatives Considered
Providers.StepRegistryas the only mechanism: Works but requires callers to repeat implementation details. Not suitable as the primary extensibility path.StepRegistry.psd1file per module: Cleaner than the parameter workaround, but unnecessary indirection when the catalog already exists and is already loaded.Impact
Handleris optional — built-in steps whose handlers are hardcoded in the registry do not need it. Fully backwards compatible.StepMetadataCatalog.psd1).Providers.StepRegistryfor well-behaved step modules.Assert-IdleNoScriptBlockguard must be extended to coverHandlervalues (must be a plain string, not a ScriptBlock).Handlerentry, which wins?Additional Context
Relevant files:
src/IdLE.Core/Private/Resolve-IdleStepMetadataCatalog.ps1— catalog loading, lines 265–358src/IdLE.Core/Private/Get-IdleStepRegistry.ps1— hardcoded registry, lines 110–221src/IdLE.Core/Private/Resolve-IdleStepHandler.ps1— two-tier handler resolutionsrc/IdLE.Steps.Common/StepMetadataCatalog.psd1— reference catalog format