Skip to content

Step types should declare their own required capabilities (metadata-driven validation) #89

@blindzero

Description

@blindzero

Goal

Remove RequiresCapabilities from workflow definitions.

From now on, each Step.Type is the single source of truth for required provider capabilities. Planning derives required capabilities from a step metadata catalog (trusted extension point provided by step packs / host), not from workflow configuration.

This is a breaking change (pre-1.0). Existing workflows, examples, and tests must be updated.

Motivation

  • Avoid duplication: the step implementation already knows which provider features it needs.
  • Reduce workflow author burden and misconfiguration risk.
  • Keep planning-time capability validation deterministic while moving requirements closer to the code that enforces them.

Proposed design

1) Workflow schema change (remove RequiresCapabilities)

  • Remove RequiresCapabilities from the list of allowed step keys in workflow schema validation.
  • Workflows that still contain RequiresCapabilities must fail validation with an actionable message.

2) Add a trusted Step Metadata Catalog

Introduce a new trusted extension point (similar to StepRegistry) that maps Step.Type to metadata.

Input location (host-controlled):

  • Providers.StepMetadata (hashtable), or Providers['StepMetadata'] (hashtable), analogous to Providers.StepRegistry.

Shape:

  • Key: Step.Type (string)
  • Value: hashtable (data-only), minimum contract:
@{
  RequiredCapabilities = @('IdLE.Identity.Disable', 'IdLE.Identity.Read')
}

Validation rules (fail fast):

  • Must be a hashtable.
  • Keys must be non-empty strings.
  • Values must be hashtables.
  • RequiredCapabilities (if present) must be:
    • missing / $null (treated as empty)
    • string
    • string array
  • Capability identifiers must follow the repo rules (dot-separated, no whitespace).
  • Reject ScriptBlocks anywhere in the metadata (data-only boundary).

3) Built-in step packs provide their metadata

  • Each first-party step pack (e.g. IdLE.Steps.Common) must expose a function that returns its metadata, for example:

    • Get-IdleStepMetadataCatalog returning a hashtable keyed by Step.Type.
  • IdLE.Core adds a helper Get-IdleStepMetadataCatalog -Providers $Providers that:

    • loads built-in step metadata catalogs (module-qualified discovery like Get-IdleStepRegistry)
    • merges host-provided Providers.StepMetadata
    • uses case-insensitive keys
    • applies deterministic precedence:
      • host metadata overrides built-in metadata for the same Step.Type

4) Planning changes (how required capabilities are derived)

  • During plan build (New-IdlePlan / New-IdlePlanObject):
    1. Resolve StepRegistry as today (trusted).
    2. Resolve StepMetadataCatalog (trusted).
    3. For each workflow step:
      • look up metadata by Step.Type
      • normalize RequiredCapabilities and attach it to the plan step as RequiresCapabilities
    4. Capability validation uses the plan step’s derived RequiresCapabilities.

Fail-fast behavior:

  • If a workflow references a Step.Type that exists in the StepRegistry but has no metadata entry, plan build fails with:
    • missing StepMetadata for Step.Type
    • guidance: host must add Providers.StepMetadata['<StepType>'] = @{ RequiredCapabilities = ... }

5) Compatibility and contracts

  • StepRegistry remains: Step.Type -> handler function name (string). No ScriptBlocks.
  • StepMetadata is a separate catalog. This keeps StepRegistry shape stable.
  • Workflow remains data-only; this change reduces “policy” surface in workflow.

Breaking changes

  • Workflow PSD1 files that contain RequiresCapabilities become invalid.
  • Hosts that provide custom steps must provide both:
    • a StepRegistry mapping (existing)
    • a StepMetadata mapping (new)

Implementation tasks

Core

  • Update workflow schema validation to reject RequiresCapabilities.
  • Add Get-IdleStepMetadataCatalog (new private helper) mirroring the style and validation approach of Get-IdleStepRegistry.
  • Update plan build:
    • remove normalization of RequiresCapabilities from workflow steps
    • derive required capabilities from StepMetadataCatalog
    • keep the plan step property name RequiresCapabilities so downstream validation and plan export remain transparent.

Docs

  • Update provider capability documentation to reflect that steps no longer declare RequiresCapabilities in workflows.
  • Update step metadata documentation to include RequiredCapabilities as a normative field.
  • Update workflow documentation / examples to remove RequiresCapabilities.

Tests

  • Refactor existing capability tests to use step metadata instead of workflow RequiresCapabilities.
  • Add tests for:
    • workflow containing RequiresCapabilities fails schema validation
    • missing StepMetadata for referenced Step.Type fails plan build
    • host metadata overrides built-in metadata
    • invalid metadata shapes (non-hashtable, ScriptBlock, invalid capability values)

Examples

  • Update sample workflows under examples/workflows/ to remove RequiresCapabilities.
  • Ensure demo runner still succeeds with built-in metadata discovery.

Generated references

  • Optional (but recommended): extend tools/Generate-IdleStepReference.ps1 to include RequiredCapabilities if the metadata is discoverable.
    • This keeps docs/reference/steps.md informative without relying on workflow files.

Acceptance criteria

  • Workflow schema rejects RequiresCapabilities with an actionable error.
  • Plan build derives step required capabilities from StepMetadataCatalog.
  • Capability validation during planning still works and remains deterministic.
  • Built-in step packs provide metadata for all first-party Step.Types.
  • All unit tests updated and green.
  • Examples updated and runnable.
  • Relevant docs updated (workflow schema, provider capabilities, step metadata guidance).

Definition of Done

  • Pester green (./tools/Invoke-IdlePesterTests.ps1)
  • PSScriptAnalyzer green (./tools/Invoke-IdleScriptAnalyzer.ps1)
  • Docs updated where contracts changed
  • Generated references regenerated if impacted (./tools/Generate-IdleStepReference.ps1)

Metadata

Metadata

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions