skills/reviewed-learning-inbox/scripts/Set-LearningCandidateStatus.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS Records a review decision on one reviewed-learning-inbox candidate. .DESCRIPTION Sets a candidate to Rejected, Superseded, or back to New with a mandatory reason. The decision is retained, so a later repeated observation increments the occurrence count without resurrecting a rejected candidate. Supersession links both candidates. A promoted candidate is immutable here, because its lesson already lives in a reviewed Customization. .PARAMETER Path Existing project directory whose Memory Bank holds the learning inbox. .PARAMETER Id Candidate identifier to decide on. .PARAMETER Status New review status: Rejected, Superseded, or New. .PARAMETER Reason Short reason recorded with the decision. .PARAMETER SupersededBy Identifier of the replacing candidate; required for Superseded. .PARAMETER ReferenceTime UTC time recorded on the decision. .EXAMPLE ./Set-LearningCandidateStatus.ps1 -Path C:/Git/MyProject -Id cand-0123456789ab ` -Status Rejected -Reason 'The lesson restates an existing rule.' Rejects a candidate and keeps the decision on record. .OUTPUTS System.Management.Automation.PSCustomObject #> [CmdletBinding(SupportsShouldProcess)] [OutputType([PSCustomObject])] param ( [Parameter()] [ValidateNotNullOrEmpty()] [ValidateScript({ Test-Path -LiteralPath $_ -PathType Container })] [string]$Path = (Get-Location).Path, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Id, [Parameter(Mandatory)] [ValidateSet('New', 'Rejected', 'Superseded')] [string]$Status, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Reason, [Parameter()] [ValidateNotNullOrEmpty()] [string]$SupersededBy, [Parameter()] [ValidateNotNull()] [datetime]$ReferenceTime = [datetime]::UtcNow ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' . (Join-Path $PSScriptRoot 'LearningInboxCommon.ps1') $context = Resolve-LearningInboxContext -Path $Path $store = Read-LearningInboxStore -Context $context if ($null -eq $store) { throw "No reviewed learning inbox was found for '$($context.RepositoryRoot)'." } $reasonText = Assert-LearningInboxText -Text $Reason -Field 'Reason' -MaximumLength 300 $candidate = Get-LearningInboxCandidate -Store $store -Id $Id if ([string]$candidate.status -eq 'Promoted') { throw "Candidate '$Id' was already promoted. Revise the destination Customization instead of rewriting the record." } $replacement = $null if ($Status -eq 'Superseded') { if (-not $PSBoundParameters.ContainsKey('SupersededBy')) { throw 'SupersededBy is required when marking a candidate as Superseded.' } if ($SupersededBy -eq $Id) { throw 'A candidate cannot supersede itself.' } $replacement = Get-LearningInboxCandidate -Store $store -Id $SupersededBy } elseif ($PSBoundParameters.ContainsKey('SupersededBy')) { throw 'SupersededBy applies only to the Superseded status.' } $timestamp = $ReferenceTime.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ') $candidate.status = $Status $candidate.updatedUtc = $timestamp $candidate.decision = [PSCustomObject][ordered]@{ status = $Status reason = $reasonText recordedUtc = $timestamp } $candidate.supersededBy = if ($Status -eq 'Superseded') { $SupersededBy } else { '' } if ($null -ne $replacement) { $replacement.supersedes = @( @($replacement.supersedes) + $Id | Select-Object -Unique ) $replacement.updatedUtc = $timestamp } $store.updatedUtc = $timestamp if ($PSCmdlet.ShouldProcess($context.StorePath, "Record '$Status' for candidate '$Id'")) { Write-LearningInboxStore -Context $context -Store $store } return $candidate |