Public/Test-SLPolicyReference.ps1
|
function Test-SLPolicyReference { <# .SYNOPSIS Audits a repo's agent entry points, shared-policy copy and status checkpoint. Read-only. .DESCRIPTION Checks, for each repo at or under -Path: entry points CLAUDE.md and AGENTS.md both exist, each has the marker lines SecondLine-Policy: <version> <path> SecondLine-Status: docs/status/current.md and both name the same policy and checkpoint policy copy the path is relative and inside the repo (portable, no developer checkout), the file exists, has a version header, matches the referenced version, is unmodified (hash), and is not older than the policy this SecondLine ships checkpoint exists, has the sections Position (Branch, Base commit), Completed, Remaining, Changed files, Verification (items labelled Verified or Planned), Blockers and Next action, and contains nothing that looks like a credential scripts vendored bootstrap copies are current; no script pins SecondLine with Import-Module -RequiredVersion (or -MinimumVersion) Errors make the repo fail. Warnings (modified or outdated copy, outdated bootstrap, hardcoded versions) are reported but don't. Nothing is changed. A checkpoint is a handoff to verify, never an authorization. .PARAMETER Path A repo, or a folder of repos. Default: the current folder. .PARAMETER Depth How many folders below -Path to look for repos. Default: 2. .PARAMETER PassThru Return one result per repo instead of $true/$false. .PARAMETER Quiet Don't print the report. .PARAMETER Strict Throw if any repo has an error. For CI. .EXAMPLE Test-SLPolicyReference -Path . -Strict #> [CmdletBinding()] [OutputType([bool], [pscustomobject])] param( [Parameter(Position = 0)] [string] $Path, [ValidateRange(0, 10)] [int] $Depth = 2, [switch] $PassThru, [switch] $Quiet, [switch] $Strict ) if (-not $Path) { $Path = (Get-Location).ProviderPath } if (-not (Test-Path -LiteralPath $Path -PathType Container)) { throw "Folder not found: $Path." } $Path = (Resolve-Path -LiteralPath $Path).ProviderPath $repos = @($Path) if (Get-Command git -CommandType Application -ErrorAction SilentlyContinue) { $found = @(Find-SLGitRepo -Path $Path -Depth $Depth) if ($found.Count -gt 0) { $repos = $found } } $checks = @(foreach ($repo in $repos) { Get-SLPolicyAudit -RepoPath $repo }) if (-not $Quiet) { Write-Host '' Write-Host " SecondLine - shared policy and status references ($Path)" -ForegroundColor White foreach ($c in $checks) { Write-Host '' $errors = @($c.Findings | Where-Object Severity -eq 'Error').Count $warnings = @($c.Findings | Where-Object Severity -eq 'Warning').Count if ($c.Ok -and $warnings -eq 0) { Write-Host " OK $($c.Repo)" -ForegroundColor Green } elseif ($c.Ok) { Write-Host " OK $($c.Repo) $warnings warning(s)" -ForegroundColor Yellow } else { Write-Host " ISSUES $($c.Repo) $errors error(s), $warnings warning(s)" -ForegroundColor Red } foreach ($f in $c.Findings) { Write-Host (" {0,-7} {1,-26} {2}: {3}" -f $f.Severity, $f.Kind, $f.File, $f.Detail) -ForegroundColor DarkGray } } Write-Host '' } $bad = @($checks | Where-Object { -not $_.Ok }) if ($Strict -and $bad.Count -gt 0) { throw "SecondLine policy references: $($bad.Count) of $($checks.Count) repos have errors: $(($bad | ForEach-Object { $_.Repo }) -join ', ')" } if ($PassThru) { return $checks } return ($bad.Count -eq 0) } |