Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 21, 2026

Summary

Step types now declare required capabilities via metadata catalogs instead of workflow configuration. RequiresCapabilities is no longer supported in workflows and is treated as an unknown key.

Motivation

Eliminates duplication between step implementations and workflow declarations. Step handlers already know which provider capabilities they require—declaring this in both places creates drift risk and workflow author burden.

Type of Change

  • Breaking change
  • New feature
  • Refactoring / internal improvement

Changes

Schema & Validation:

  • Test-IdleWorkflowSchema: RequiresCapabilities treated as unknown key (no special validation needed - pre-1.0)
  • Extracted Test-IdleWorkflowStepKeys helper function to eliminate duplicate validation logic
  • Plan building fails fast if step type has no metadata entry

Step Metadata System:

  • Get-IdleStepMetadataCatalog (IdLE.Steps.Common): Returns metadata for 8 built-in step types
  • Resolve-IdleStepMetadataCatalog (IdLE.Core): Merges built-in + host-provided metadata with reduced nesting (2-3 levels vs. original 5-6)
  • Extracted validation helpers: Test-IdleCapabilityIdentifier, Test-IdleRequiredCapabilities, Get-IdleHostStepMetadata, Test-IdleMetadataForScriptBlocks
  • Module-scoped function resolution prevents name shadowing from other modules
  • Host can override via Providers.StepMetadata hashtable
  • Validates data-only constraint (recursively rejects ScriptBlocks at any nesting level)
  • Type-safe RequiredCapabilities validation (rejects hashtables and non-string enumerables)

Plan Building:

  • New-IdlePlanObject: Derives RequiresCapabilities from metadata during planning
  • Plan step property unchanged—capability validation remains transparent
  • Improved error messages for missing metadata entries

Test Infrastructure:

  • Created tests/fixtures/workflows/ directory structure
  • Moved 9 workflow test definitions from inline Set-Content to dedicated fixture files
  • Improved test maintainability and reusability

Breaking Changes:

  • All workflow files: Remove RequiresCapabilities from step definitions (treated as unknown key)
  • Custom step providers: Must provide both StepRegistry and StepMetadata

Code Quality Improvements:

  • Reduced nesting in Resolve-IdleStepMetadataCatalog from 5-6 levels to 2-3 levels
  • Removed duplicate step validation code (~15 lines)
  • Removed backward compatibility handling (pre-1.0 - feature never existed publicly)
  • Contract alignment: Removed IdLE.Identity.Read from DisableIdentity/EnableIdentity (not required by step contracts)

Known Limitations:

  • Static metadata declares the superset of capabilities a step type might need across all invocations
  • Steps with parameter-dependent capability requirements (e.g., EnsureEntitlement with State=Present vs Absent) declare all possible capabilities
  • This is an architectural trade-off: metadata is data-only and evaluated at planning time before step parameters are resolved

Example migration:

# Before (invalid - unknown key)
@{
  Name = 'Disable user'
  Type = 'IdLE.Step.DisableIdentity'
  RequiresCapabilities = @('IdLE.Identity.Disable')  # ❌ Unknown key
}

# After (automatic from metadata)
@{
  Name = 'Disable user'
  Type = 'IdLE.Step.DisableIdentity'  # ✅ Capabilities derived from metadata
}

# Host providing custom steps
$providers = @{
  StepRegistry = @{ 'Custom.Step' = 'Invoke-CustomStep' }
  StepMetadata = @{ 'Custom.Step' = @{ RequiredCapabilities = @('Custom.Cap') } }
}

Testing

  • Unit tests: 196/196 tests passing (9 capability validation tests)
  • Manual testing: All 11 example workflows validated successfully
  • Code refactoring validated with full test suite
  • Test fixtures created for improved maintainability
  • PSScriptAnalyzer: Clean (no blocking findings)
  • Integration testing: Verified capabilities correctly derived from metadata

How to test & review

  1. Verify workflow schema treats RequiresCapabilities as unknown key
  2. Confirm plan building derives capabilities from metadata
  3. Check host metadata override precedence
  4. Validate ScriptBlock rejection in metadata (recursive validation)
  5. Validate type safety for RequiredCapabilities (rejects hashtables)
  6. Review reduced nesting and extracted helpers for maintainability
  7. Examine test fixtures structure in tests/fixtures/workflows/
  8. Run ./tools/Invoke-IdlePesterTests.ps1 to verify all tests pass
  9. Run ./tools/Invoke-IdleScriptAnalyzer.ps1 to verify code quality
  10. Validate example workflows with Test-IdleWorkflow

Checklist

  • Code follows STYLEGUIDE.md
  • Tests added or updated
  • Documentation updated (pending: workflow/provider capability docs)
  • No UI/auth logic added to IdLE.Core
  • No breaking changes without discussion (pre-1.0 breaking change)
Original prompt

This section details on the original issue you should resolve

<issue_title>Step types should declare their own required capabilities (metadata-driven validation)</issue_title>
<issue_description>## 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 StepM...

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits January 21, 2026 16:10
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
- Added New-IdleTestStepMetadata helper function to _testHelpers.ps1
- Updated Export-IdlePlan.Tests.ps1 to add metadata for 'EnsureMailbox'
- Updated Invoke-IdlePlan.Retry.Tests.ps1 to add metadata for 'IdLE.Step.Transient' and 'IdLE.Step.NonTransient'
- Updated Invoke-IdlePlan.Tests.ps1 to add metadata for multiple step types
- Updated ModuleSurface.Tests.ps1 to add metadata for 'IdLE.Step.Primary' and 'IdLE.Step.Containment'
- Updated New-IdlePlan.Tests.ps1 to add metadata for step types in OnFailureSteps tests
- Fixed instances where New-IdlePlan was called without -Providers parameter

All affected tests now pass.
Copilot AI changed the title [WIP] Remove RequiresCapabilities from workflow definitions Implement metadata-driven step capabilities (breaking change) Jan 21, 2026
Copilot AI requested a review from blindzero January 21, 2026 16:58
Copilot AI and others added 2 commits January 21, 2026 18:43
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copy link
Owner

@blindzero blindzero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot please check in all Pester tests occurances of Set-Content if these should be put into tests/fixtures.

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 21, 2026

@copilot please check in all Pester tests occurances of Set-Content if these should be put into tests/fixtures.

Fixed in commit 86e5f72. Created tests/fixtures/workflows/ directory and moved all 9 workflow definitions from New-IdlePlan.Capabilities.Tests.ps1 to fixture files. Tests now reference fixture files instead of using inline Set-Content.

@blindzero
Copy link
Owner

@copilot make sure all Pester + PSSA are green and examples are all running

Copy link
Contributor Author

Copilot AI commented Jan 21, 2026

@copilot make sure all Pester + PSSA are green and examples are all running

All checks passing:

Pester Tests: 196/196 passed (0 failed)
PSScriptAnalyzer: Clean (no blocking findings)
Example Workflows: All 11 workflows validate successfully

  • ad-joiner-complete.psd1
  • ad-leaver-offboarding.psd1
  • ad-mover-department-change.psd1
  • joiner-ensureentitlement.psd1
  • joiner-with-onfailure.psd1
  • joiner-minimal.psd1
  • joiner-minimal-ensureattribute.psd1
  • joiner-with-condition.psd1
  • entraid-joiner-complete.psd1
  • entraid-leaver-offboarding.psd1
  • entraid-mover-department-change.psd1
    Plan Creation: Verified capabilities correctly derived from metadata (e.g., EnsureAttribute requires IdLE.Identity.Attribute.Ensure, EnsureEntitlement requires IdLE.Entitlement.List/Grant/Revoke)

@blindzero blindzero marked this pull request as ready for review January 21, 2026 19:35
Copilot AI review requested due to automatic review settings January 21, 2026 19:35
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86e5f72c57

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements metadata-driven required capability derivation for workflow steps, removing RequiresCapabilities from workflow definitions and shifting requirements into a trusted step metadata catalog.

Changes:

  • Added a built-in + host-overridable StepMetadata catalog and merged resolver to derive RequiresCapabilities during planning.
  • Updated workflow schema validation to treat RequiresCapabilities as an unknown step key.
  • Refactored tests to use workflow fixtures and updated test providers to supply StepMetadata.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/fixtures/workflows/joiner-scriptblock.psd1 New workflow fixture used for metadata ScriptBlock rejection coverage.
tests/fixtures/workflows/joiner-override.psd1 New fixture to validate host metadata override behavior.
tests/fixtures/workflows/joiner-onfailure.psd1 New fixture to validate OnFailureSteps capability derivation.
tests/fixtures/workflows/joiner-no-metadata.psd1 New fixture to validate fail-fast behavior for missing step metadata.
tests/fixtures/workflows/joiner-missing-caps.psd1 New fixture to validate fail-fast behavior for missing provider capabilities.
tests/fixtures/workflows/joiner-invalid.psd1 New fixture used for invalid metadata shape tests.
tests/fixtures/workflows/joiner-invalid-cap.psd1 New fixture used for invalid capability identifier tests.
tests/fixtures/workflows/joiner-entitlements.psd1 New fixture to validate entitlement capability derivation.
tests/fixtures/workflows/joiner-builtin.psd1 New fixture to validate built-in metadata derivation.
tests/_testHelpers.ps1 Added helper to generate StepMetadata for test step types.
tests/New-IdlePlan.Tests.ps1 Updated tests to provide StepRegistry + StepMetadata where needed.
tests/New-IdlePlan.Capabilities.Tests.ps1 Reworked capability tests to derive requirements from step metadata + fixtures.
tests/ModuleSurface.Tests.ps1 Updated plan-building test to provide StepMetadata for custom steps.
tests/Invoke-IdlePlan.Tests.ps1 Updated execution tests to provide StepMetadata alongside StepRegistry.
tests/Invoke-IdlePlan.Retry.Tests.ps1 Updated retry tests to import helpers and provide StepMetadata.
tests/Export-IdlePlan.Tests.ps1 Updated export tests to provide StepMetadata for custom step types.
src/IdLE.Steps.Common/Public/Get-IdleStepMetadataCatalog.ps1 New built-in metadata catalog for first-party step types.
src/IdLE.Steps.Common/IdLE.Steps.Common.psm1 Exported the new Get-IdleStepMetadataCatalog function.
src/IdLE.Steps.Common/IdLE.Steps.Common.psd1 Added Get-IdleStepMetadataCatalog to exported functions list.
src/IdLE.Core/Public/New-IdlePlanObject.ps1 Planning now resolves StepMetadata catalog and derives RequiresCapabilities from it.
src/IdLE.Core/Private/Test-IdleWorkflowSchema.ps1 Removed RequiresCapabilities from allowed step keys (now unknown key).
src/IdLE.Core/Private/Resolve-IdleStepMetadataCatalog.ps1 New resolver that merges built-in and host metadata with validation.
examples/workflows/joiner-with-onfailure.psd1 Removed RequiresCapabilities from example workflow steps.
examples/workflows/joiner-ensureentitlement.psd1 Removed RequiresCapabilities from example workflow steps.
examples/workflows/ad-mover-department-change.psd1 Removed RequiresCapabilities from example workflow steps.
examples/workflows/ad-leaver-offboarding.psd1 Removed RequiresCapabilities from example workflow steps.
examples/workflows/ad-joiner-complete.psd1 Removed RequiresCapabilities from example workflow steps.

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
@blindzero blindzero merged commit 3a6e39c into main Jan 21, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants