From de7c4bcca74ddf86a4f5fd223be09bd9ca69cc5e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 07:09:12 +0000 Subject: [PATCH 1/5] Initial plan From 9bc2a120d1385e0c3c34b4c2af0f61d3f56b6d58 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 07:14:46 +0000 Subject: [PATCH 2/5] Add comprehensive comment-based help to New-IdleLifecycleRequestObject Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .../Public/New-IdleLifecycleRequestObject.ps1 | 69 +++++++++++++++++++ tests/Core/ModuleExports.Tests.ps1 | 27 ++++++++ 2 files changed, 96 insertions(+) diff --git a/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 b/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 index 06441b8b..3eec7f4f 100644 --- a/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 +++ b/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 @@ -1,4 +1,73 @@ function New-IdleLifecycleRequestObject { + <# + .SYNOPSIS + Creates a lifecycle request object (core factory). + + .DESCRIPTION + Constructs and returns an IdleLifecycleRequest domain object representing business intent + (e.g. Joiner/Mover/Leaver). This is the core factory function used by the IdLE module wrapper. + + The function validates that no ScriptBlocks are present in the input data (IdentityKeys, + DesiredState, Changes) to enforce the data-only configuration principle. Input hashtables + are cloned to prevent external mutation after object creation. + + CorrelationId is preserved if provided; otherwise, the IdleLifecycleRequest class generates + a new GUID. Actor is optional and not required by the core engine. + + .PARAMETER LifecycleEvent + The lifecycle event name (e.g. Joiner, Mover, Leaver). This is a required string that + identifies the type of lifecycle operation being requested. + + .PARAMETER CorrelationId + Optional correlation identifier for audit and event correlation. If omitted, a GUID is + automatically generated by the IdleLifecycleRequest constructor. + + .PARAMETER Actor + Optional actor claim identifying who initiated the request. Not required by the core + engine in V1 but may be used for audit logging or workflow-specific logic. + + .PARAMETER IdentityKeys + A hashtable of system-neutral identity keys (e.g. @{ EmployeeId = '12345'; UPN = 'user@contoso.com' }). + Defaults to an empty hashtable if not provided. Must not contain ScriptBlocks. + + .PARAMETER DesiredState + A hashtable describing the desired state for the identity (attributes, entitlements, etc.). + Defaults to an empty hashtable if not provided. Must not contain ScriptBlocks. + + .PARAMETER Changes + Optional hashtable describing changes (typically used for Mover lifecycle events to indicate + what changed from the previous state). Remains $null when omitted. Must not contain ScriptBlocks. + + .EXAMPLE + $request = New-IdleLifecycleRequestObject -LifecycleEvent 'Joiner' + + Creates a minimal Joiner request with auto-generated CorrelationId and empty IdentityKeys/DesiredState. + + .EXAMPLE + $request = New-IdleLifecycleRequestObject -LifecycleEvent 'Joiner' -CorrelationId (New-Guid).Guid -IdentityKeys @{ EmployeeId = '12345'; Department = 'Engineering' } -DesiredState @{ MailNickname = 'jdoe'; Title = 'Engineer' } + + Creates a Joiner request with specific identity keys and desired state attributes for a typical onboarding workflow. + + .EXAMPLE + $request = New-IdleLifecycleRequestObject -LifecycleEvent 'Mover' -IdentityKeys @{ UPN = 'user@contoso.com' } -Changes @{ Department = 'Sales' } -Actor 'admin@contoso.com' + + Creates a Mover request with identity keys, changes, and actor information for a department transfer workflow. + + .OUTPUTS + IdleLifecycleRequest + + .NOTES + Security Considerations: + - Input data must be data-only (no ScriptBlocks or executable objects). The function + validates this constraint and throws if violated. + - Do not embed secrets in IdentityKeys, DesiredState, or Changes. Use the AuthSessionBroker + pattern for credential/token management. + - Sensitive data in request objects may be logged or emitted in events. Rely on redaction + boundaries defined in the engine's event sink and logging layers. + + This is a core engine function. For the user-facing API, use New-IdleLifecycleRequest from + the IdLE module, which delegates to this function. + #> [CmdletBinding()] param( [Parameter(Mandatory)] diff --git a/tests/Core/ModuleExports.Tests.ps1 b/tests/Core/ModuleExports.Tests.ps1 index 13d87313..81441aa2 100644 --- a/tests/Core/ModuleExports.Tests.ps1 +++ b/tests/Core/ModuleExports.Tests.ps1 @@ -62,6 +62,33 @@ Describe 'Module Export Consistency' { } } } + + It 'New-IdleLifecycleRequestObject has comment-based help (Synopsis + Description + Examples)' { + $cmd = Get-Command -Name 'New-IdleLifecycleRequestObject' -Module IdLE.Core -ErrorAction Stop + + $help = Get-Help -Name $cmd.Name -ErrorAction Stop + + # Synopsis + $help.Synopsis | Should -Not -BeNullOrEmpty -Because "Function '$($cmd.Name)' should have a Synopsis" + + # Description (can be structured) + $descText = + if ($help.Description -and $help.Description.Text) { ($help.Description.Text -join "`n").Trim() } + else { '' } + + $descText | Should -Not -BeNullOrEmpty -Because "Function '$($cmd.Name)' should have a Description" + + # Examples (can also be structured) + $exampleCount = + if ($help.Examples -and $help.Examples.Example) { + @($help.Examples.Example).Count + } + else { + 0 + } + + $exampleCount | Should -BeGreaterThan 0 -Because "Function '$($cmd.Name)' should have at least one Example" + } } Context 'IdLE meta-module exports' { From 0679c4c20ed973b16b3f78286497ab9ffab5608d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:32:04 +0000 Subject: [PATCH 3/5] Make CBH test general for all IdLE.Core public functions and add missing examples Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .../Public/Invoke-IdlePlanObject.ps1 | 5 +++ src/IdLE.Core/Public/New-IdlePlanObject.ps1 | 5 +++ .../Test-IdleWorkflowDefinitionObject.ps1 | 5 +++ tests/Core/ModuleExports.Tests.ps1 | 41 ++++++++++--------- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1 b/src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1 index cba6d9b8..57bd50be 100644 --- a/src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1 +++ b/src/IdLE.Core/Public/Invoke-IdlePlanObject.ps1 @@ -32,6 +32,11 @@ function Invoke-IdlePlanObject { .OUTPUTS PSCustomObject (PSTypeName: IdLE.ExecutionResult) + + .EXAMPLE + $result = Invoke-IdlePlanObject -Plan $plan -Providers $providers + + Executes a plan with the specified provider registry and returns an execution result. #> [CmdletBinding()] param( diff --git a/src/IdLE.Core/Public/New-IdlePlanObject.ps1 b/src/IdLE.Core/Public/New-IdlePlanObject.ps1 index b1ade006..a1dedeb6 100644 --- a/src/IdLE.Core/Public/New-IdlePlanObject.ps1 +++ b/src/IdLE.Core/Public/New-IdlePlanObject.ps1 @@ -24,6 +24,11 @@ function New-IdlePlanObject { .OUTPUTS PSCustomObject (PSTypeName: IdLE.Plan) + + .EXAMPLE + $plan = New-IdlePlanObject -WorkflowPath ./workflows/joiner.psd1 -Request $request -Providers $providers + + Builds a plan from a workflow definition and lifecycle request with the specified provider registry. #> [CmdletBinding()] param( diff --git a/src/IdLE.Core/Public/Test-IdleWorkflowDefinitionObject.ps1 b/src/IdLE.Core/Public/Test-IdleWorkflowDefinitionObject.ps1 index 60fc3be2..25f52b9f 100644 --- a/src/IdLE.Core/Public/Test-IdleWorkflowDefinitionObject.ps1 +++ b/src/IdLE.Core/Public/Test-IdleWorkflowDefinitionObject.ps1 @@ -15,6 +15,11 @@ function Test-IdleWorkflowDefinitionObject { .OUTPUTS PSCustomObject (PSTypeName: IdLE.WorkflowDefinition) + + .EXAMPLE + $workflow = Test-IdleWorkflowDefinitionObject -WorkflowPath ./workflows/joiner.psd1 + + Loads and validates a workflow definition, ensuring it is data-only and schema-compliant. #> [CmdletBinding()] param( diff --git a/tests/Core/ModuleExports.Tests.ps1 b/tests/Core/ModuleExports.Tests.ps1 index 81441aa2..7054d4ca 100644 --- a/tests/Core/ModuleExports.Tests.ps1 +++ b/tests/Core/ModuleExports.Tests.ps1 @@ -63,31 +63,34 @@ Describe 'Module Export Consistency' { } } - It 'New-IdleLifecycleRequestObject has comment-based help (Synopsis + Description + Examples)' { - $cmd = Get-Command -Name 'New-IdleLifecycleRequestObject' -Module IdLE.Core -ErrorAction Stop + It 'Exported IdLE.Core functions have comment-based help (Synopsis + Description + Examples)' { + $commands = Get-Command -Module IdLE.Core -CommandType Function + $commands | Should -Not -BeNullOrEmpty - $help = Get-Help -Name $cmd.Name -ErrorAction Stop + foreach ($cmd in $commands) { + $help = Get-Help -Name $cmd.Name -ErrorAction Stop - # Synopsis - $help.Synopsis | Should -Not -BeNullOrEmpty -Because "Function '$($cmd.Name)' should have a Synopsis" + # Synopsis + $help.Synopsis | Should -Not -BeNullOrEmpty -Because "Function '$($cmd.Name)' should have a Synopsis" - # Description (can be structured) - $descText = - if ($help.Description -and $help.Description.Text) { ($help.Description.Text -join "`n").Trim() } - else { '' } + # Description (can be structured) + $descText = + if ($help.Description -and $help.Description.Text) { ($help.Description.Text -join "`n").Trim() } + else { '' } - $descText | Should -Not -BeNullOrEmpty -Because "Function '$($cmd.Name)' should have a Description" + $descText | Should -Not -BeNullOrEmpty -Because "Function '$($cmd.Name)' should have a Description" - # Examples (can also be structured) - $exampleCount = - if ($help.Examples -and $help.Examples.Example) { - @($help.Examples.Example).Count - } - else { - 0 - } + # Examples (can also be structured) + $exampleCount = + if ($help.Examples -and $help.Examples.Example) { + @($help.Examples.Example).Count + } + else { + 0 + } - $exampleCount | Should -BeGreaterThan 0 -Because "Function '$($cmd.Name)' should have at least one Example" + $exampleCount | Should -BeGreaterThan 0 -Because "Function '$($cmd.Name)' should have at least one Example" + } } } From 35361db18f68e515ba33f748c333ab19462ec958 Mon Sep 17 00:00:00 2001 From: Matthias <13959569+blindzero@users.noreply.github.com> Date: Fri, 13 Feb 2026 09:51:46 +0100 Subject: [PATCH 4/5] Update src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 b/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 index 3eec7f4f..dfd66eb8 100644 --- a/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 +++ b/src/IdLE.Core/Public/New-IdleLifecycleRequestObject.ps1 @@ -44,7 +44,7 @@ function New-IdleLifecycleRequestObject { Creates a minimal Joiner request with auto-generated CorrelationId and empty IdentityKeys/DesiredState. .EXAMPLE - $request = New-IdleLifecycleRequestObject -LifecycleEvent 'Joiner' -CorrelationId (New-Guid).Guid -IdentityKeys @{ EmployeeId = '12345'; Department = 'Engineering' } -DesiredState @{ MailNickname = 'jdoe'; Title = 'Engineer' } + $request = New-IdleLifecycleRequestObject -LifecycleEvent 'Joiner' -CorrelationId (New-Guid).Guid -IdentityKeys @{ EmployeeId = '12345' } -DesiredState @{ Department = 'Engineering'; MailNickname = 'jdoe'; Title = 'Engineer' } Creates a Joiner request with specific identity keys and desired state attributes for a typical onboarding workflow. From 169b3a280cdcf8a662db0c451b862962bd9bb4ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 08:55:55 +0000 Subject: [PATCH 5/5] Add parameter documentation validation to CBH test Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- tests/Core/ModuleExports.Tests.ps1 | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/Core/ModuleExports.Tests.ps1 b/tests/Core/ModuleExports.Tests.ps1 index 7054d4ca..d4310677 100644 --- a/tests/Core/ModuleExports.Tests.ps1 +++ b/tests/Core/ModuleExports.Tests.ps1 @@ -63,7 +63,7 @@ Describe 'Module Export Consistency' { } } - It 'Exported IdLE.Core functions have comment-based help (Synopsis + Description + Examples)' { + It 'Exported IdLE.Core functions have comment-based help (Synopsis + Description + Examples + Parameters)' { $commands = Get-Command -Module IdLE.Core -CommandType Function $commands | Should -Not -BeNullOrEmpty @@ -90,6 +90,27 @@ Describe 'Module Export Consistency' { } $exampleCount | Should -BeGreaterThan 0 -Because "Function '$($cmd.Name)' should have at least one Example" + + # Parameters - check that each non-common parameter has documentation + # Common parameters (Debug, ErrorAction, etc.) are automatically documented by PowerShell + $commonParameters = @( + 'Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'InformationAction', + 'ErrorVariable', 'WarningVariable', 'InformationVariable', 'OutVariable', + 'OutBuffer', 'PipelineVariable', 'ProgressAction' + ) + + $cmdParameters = @($cmd.Parameters.Keys | Where-Object { $_ -notin $commonParameters }) + + if ($cmdParameters.Count -gt 0) { + $helpParameters = @() + if ($help.parameters -and $help.parameters.parameter) { + $helpParameters = @($help.parameters.parameter | ForEach-Object { $_.name }) + } + + foreach ($paramName in $cmdParameters) { + $helpParameters | Should -Contain $paramName -Because "Function '$($cmd.Name)' should have .PARAMETER documentation for parameter '$paramName'" + } + } } } }