Private/Get-KadenRepoHealthFinding.ps1

function Get-KadenRepoHealthFinding {
    <#
    .SYNOPSIS
        Collect read-only health findings for a child repository.
    .DESCRIPTION
        Reads the seed ignore list through Get-KadenRequiredIgnoreLine and the
        doc-scaffold inventory. Findings use Area Ignore, Hooks, DocScaffold,
        or AgentsInjection. Checks those surfaces plus hook files and the
        AGENTS.md marker pair. An AGENTS.md larger than 1 MB is not scanned.
        Does not create or edit files.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$ChildRoot
    )

    $seedRoot = Join-Path $script:KadenModuleRoot 'Seeds'
    $manifestPath = Join-Path $seedRoot 'manifest.psd1'
    if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
        throw 'KDN-SEED-MISSING: Seeds/manifest.psd1 is not in the module.'
    }
    $manifest = Import-PowerShellDataFile -Path $manifestPath
    $requiredIgnore = @(Get-KadenRequiredIgnoreLine -SeedRoot $seedRoot -Manifest $manifest)
    $docs = @($manifest.Docs)
    if ($docs.Count -lt 1) {
        throw 'KDN-SEED-MISSING: Seeds/manifest.psd1 has no Docs entries.'
    }

    $findings = New-Object System.Collections.Generic.List[object]

    $gitignorePath = Resolve-KadenContainedPath -Root $ChildRoot -RelativePath '.gitignore'
    $existingIgnore = @()
    if (Test-Path -LiteralPath $gitignorePath -PathType Leaf) {
        $existingIgnore = @(Get-Content -LiteralPath $gitignorePath)
    }
    foreach ($line in $requiredIgnore) {
        $present = $false
        foreach ($existingLine in $existingIgnore) {
            if ([string]::Equals($existingLine, $line, [System.StringComparison]::Ordinal)) {
                $present = $true
                break
            }
        }
        $detail = if ($present) { "Ignore entry $line is present." } else { "Missing ignore entry $line." }
        $findings.Add([pscustomobject]@{
                Area   = 'Ignore'
                Name   = $line
                Passed = $present
                Detail = $detail
            })
    }

    $hookPaths = @('.cursor/hooks.json', '.github/hooks/harness.json')
    foreach ($relative in $hookPaths) {
        $full = Resolve-KadenContainedPath -Root $ChildRoot -RelativePath $relative
        $present = Test-Path -LiteralPath $full -PathType Leaf
        $detail = if ($present) { "Hook file $relative is present." } else { "Missing hook file $relative." }
        $findings.Add([pscustomobject]@{
                Area   = 'Hooks'
                Name   = $relative
                Passed = $present
                Detail = $detail
            })
    }

    foreach ($relative in $docs) {
        $scaffold = [string]$relative
        $full = Resolve-KadenContainedPath -Root $ChildRoot -RelativePath $scaffold
        $present = Test-Path -LiteralPath $full -PathType Leaf
        $detail = if ($present) { "Doc scaffold $scaffold is present." } else { "Missing doc scaffold $scaffold." }
        $findings.Add([pscustomobject]@{
                Area   = 'DocScaffold'
                Name   = $scaffold
                Passed = $present
                Detail = $detail
            })
    }

    $startToken = '<!-- KADEN:START -->'
    $endToken = '<!-- KADEN:END -->'
    $agentsPath = Resolve-KadenContainedPath -Root $ChildRoot -RelativePath 'AGENTS.md'
    $agentsPassed = $false
    $agentsDetail = 'AGENTS.md is missing the Kaden marker pair.'
    $agentsMaxBytes = 1MB
    if (-not (Test-Path -LiteralPath $agentsPath -PathType Leaf)) {
        $agentsDetail = 'AGENTS.md is missing.'
    }
    elseif ((Get-Item -LiteralPath $agentsPath -Force).Length -gt $agentsMaxBytes) {
        $agentsDetail = 'AGENTS.md is larger than 1 MB, so the marker pair was not scanned.'
    }
    else {
        $agentsText = Get-Content -LiteralPath $agentsPath -Raw
        if ($null -eq $agentsText) {
            $agentsText = ''
        }
        $startAt = $agentsText.IndexOf($startToken, [System.StringComparison]::Ordinal)
        $endAt = $agentsText.IndexOf($endToken, [System.StringComparison]::Ordinal)
        if ($startAt -ge 0 -and $endAt -gt $startAt) {
            $agentsPassed = $true
            $agentsDetail = 'AGENTS.md contains the Kaden marker pair.'
        }
    }
    $findings.Add([pscustomobject]@{
            Area   = 'AgentsInjection'
            Name   = 'AGENTS.md'
            Passed = $agentsPassed
            Detail = $agentsDetail
        })

    return @($findings.ToArray())
}