param(
  [string]$ProjectRoot = "",
  [string]$TaskFile = "task.json",
  [string]$DefaultRunProfile = ".codex\\task-run-profile.json"
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

if ([string]::IsNullOrWhiteSpace($ProjectRoot)) {
  $ProjectRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\.."))
}

function Test-JsonFile {
  param([string]$Path)

  try {
    Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json | Out-Null
    return $true
  }
  catch {
    return $false
  }
}

function Read-JsonFileOrNull {
  param([string]$Path)

  try {
    return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json
  }
  catch {
    return $null
  }
}

function Get-ExistingRelativePath {
  param(
    [string]$Root,
    [string]$RelativePath
  )

  $fullPath = Join-Path $Root $RelativePath
  if (Test-Path -LiteralPath $fullPath) {
    return $fullPath
  }

  return $null
}

function Add-DoctorError {
  param(
    [ref]$Errors,
    [string]$Message
  )

  $Errors.Value += $Message
}

function Add-DoctorWarning {
  param(
    [ref]$Warnings,
    [string]$Message
  )

  $Warnings.Value += $Message
}

function Invoke-TaskStructureValidation {
  param(
    [string]$Root,
    [ref]$Errors,
    [ref]$Warnings
  )

  $scriptPath = Join-Path $Root "tools\harness\task-structure-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath -PathType Leaf)) {
    Add-DoctorError -Errors $Errors -Message "缺少 task structure lint: tools\\harness\\task-structure-lint.ps1"
    return
  }

  try {
    $result = & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root -JsonOutput | ConvertFrom-Json
  }
  catch {
    Add-DoctorError -Errors $Errors -Message "task structure lint 执行失败: $($_.Exception.Message)"
    return
  }

  foreach ($finding in @($result.findings)) {
    $message = [string]$finding.message
    if (-not [string]::IsNullOrWhiteSpace([string]$finding.task_id)) {
      $message = "task $($finding.task_id): $message"
    }

    if ($finding.severity -eq "warning") {
      Add-DoctorWarning -Warnings $Warnings -Message $message
    }
    else {
      Add-DoctorError -Errors $Errors -Message $message
    }
  }
}

function Get-DoctorFileSha256 {
  param([string]$Path)

  $sha256 = [System.Security.Cryptography.SHA256]::Create()
  $stream = $null
  try {
    $stream = [System.IO.File]::OpenRead($Path)
    $hashBytes = $sha256.ComputeHash($stream)
    return (($hashBytes | ForEach-Object { $_.ToString("x2") }) -join "")
  }
  finally {
    if ($null -ne $stream) {
      $stream.Dispose()
    }
    $sha256.Dispose()
  }
}

function Test-TaskFileHasTemplatePlaceholder {
  param([string]$Content)

  $placeholderPatterns = @(
    "<replace-",
    "FR-REPLACE",
    "REQ-REPLACE",
    "NFR-REPLACE",
    "META-HANDOFF-REPLACE"
  )

  foreach ($pattern in $placeholderPatterns) {
    if ($Content.Contains($pattern)) {
      return $true
    }
  }

  return $false
}

function Normalize-DoctorManifestPath {
  param([string]$Path)

  $raw = if ($null -eq $Path) { "" } else { [string]$Path }
  $normalized = $raw.Trim().Replace('\', '/')
  while ($normalized.StartsWith('./', [System.StringComparison]::Ordinal)) {
    $normalized = $normalized.Substring(2)
  }

  return $normalized.TrimStart('/').TrimEnd('/')
}

function Test-DoctorManifestPathHasParentSegment {
  param([string]$Path)

  $body = (Normalize-DoctorManifestPath -Path $Path).TrimEnd('/')
  if ([string]::IsNullOrWhiteSpace($body)) {
    return $false
  }

  foreach ($segment in ($body -split '/')) {
    if ($segment -eq '..') {
      return $true
    }
  }

  return $false
}

function Test-DoctorManifestPathRooted {
  param([string]$Path)

  $raw = if ($null -eq $Path) { "" } else { [string]$Path }
  $trimmed = $raw.Trim()
  return [System.IO.Path]::IsPathRooted($trimmed) -or ($trimmed -match '^[A-Za-z]:')
}

function Resolve-DoctorManifestSourcePath {
  param(
    [string]$Root,
    [string]$ManifestDirectory,
    [string]$SourceRelativePath
  )

  $normalizedSource = Normalize-DoctorManifestPath -Path $SourceRelativePath
  $candidatePaths = New-Object System.Collections.Generic.List[string]
  if (
    $normalizedSource.Equals('templates', [System.StringComparison]::OrdinalIgnoreCase) -or
    $normalizedSource.StartsWith('templates/', [System.StringComparison]::OrdinalIgnoreCase)
  ) {
    $templateRelative = $normalizedSource.Replace('/', '\')
    $candidatePaths.Add((Join-Path $ManifestDirectory ("docs\codex-harness-engineering\" + $templateRelative)))
  }

  $candidatePaths.Add((Join-Path $ManifestDirectory ($normalizedSource.Replace('/', '\'))))
  $candidatePaths.Add((Join-Path $Root ($normalizedSource.Replace('/', '\'))))

  foreach ($candidatePath in @($candidatePaths | Select-Object -Unique)) {
    if (Test-Path -LiteralPath $candidatePath) {
      return $candidatePath
    }
  }

  return $null
}

function Test-InstallManifestGovernance {
  param(
    [string]$Root,
    [object]$Manifest,
    [string]$ManifestPath,
    [string]$ManifestRelativePath,
    [ref]$Errors,
    [ref]$Warnings
  )

  if ([string]$Manifest.schemaVersion -notmatch '^[0-9]+\.[0-9]+$') {
    Add-DoctorError -Errors $Errors -Message "install manifest schemaVersion 必须使用 major.minor 格式: $ManifestRelativePath"
  }

  $requiredModes = @("full", "user", "project", "vendor")
  $knownModes = @{}
  foreach ($mode in @($Manifest.installModes)) {
    $modeName = [string]$mode.name
    if ([string]::IsNullOrWhiteSpace($modeName)) {
      Add-DoctorError -Errors $Errors -Message "install manifest 存在空 install mode name: $ManifestRelativePath"
      continue
    }

    if ($knownModes.ContainsKey($modeName)) {
      Add-DoctorError -Errors $Errors -Message "install manifest 存在重复 install mode: $modeName"
    }
    else {
      $knownModes[$modeName] = $true
    }

    if ($requiredModes -notcontains $modeName) {
      Add-DoctorError -Errors $Errors -Message "install manifest 使用未知 install mode: $modeName"
    }
  }

  foreach ($requiredMode in $requiredModes) {
    if (-not $knownModes.ContainsKey($requiredMode)) {
      Add-DoctorError -Errors $Errors -Message "install manifest 缺少 install mode: $requiredMode"
    }
  }

  $manifestDirectory = Split-Path -Parent $ManifestPath
  $allowedScopes = @("project", "user", "vendor")
  $destinationKeys = @{}
  $scopeCounts = @{
    project = 0
    user = 0
    vendor = 0
  }

  $entries = @($Manifest.entries)
  for ($index = 0; $index -lt $entries.Count; $index++) {
    $entry = $entries[$index]
    $sourceRaw = [string]$entry.source
    $destinationRaw = [string]$entry.destination
    $scope = [string]$entry.scope
    $kind = [string]$entry.kind
    $source = Normalize-DoctorManifestPath -Path $sourceRaw
    $destination = Normalize-DoctorManifestPath -Path $destinationRaw

    if ($allowedScopes -notcontains $scope) {
      Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].scope 不支持: $scope"
      continue
    }

    $scopeCounts[$scope] += 1

    if ([string]::IsNullOrWhiteSpace($kind)) {
      Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].kind 不能为空。"
    }

    if ([string]::IsNullOrWhiteSpace($source)) {
      Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].source 不能为空。"
    }
    else {
      if ($sourceRaw.Contains('\')) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].source 必须使用 / 分隔: $sourceRaw"
      }

      if ((Test-DoctorManifestPathRooted -Path $sourceRaw) -or (Test-DoctorManifestPathHasParentSegment -Path $sourceRaw)) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].source 必须是包内相对路径: $sourceRaw"
      }
    }

    if ([string]::IsNullOrWhiteSpace($destination)) {
      Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].destination 不能为空。"
    }
    elseif ($scope -eq "user") {
      if ($destinationRaw.Contains('\')) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].destination 必须使用 / 分隔: $destinationRaw"
      }

      if (-not $destination.StartsWith('%USERPROFILE%/', [System.StringComparison]::OrdinalIgnoreCase)) {
        Add-DoctorError -Errors $Errors -Message "install manifest user destination 必须以 %USERPROFILE%/ 开头: $destinationRaw"
      }

      if (Test-DoctorManifestPathHasParentSegment -Path $destinationRaw) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].destination 不能包含 .. 段: $destinationRaw"
      }
    }
    else {
      if ($destinationRaw.Contains('\')) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].destination 必须使用 / 分隔: $destinationRaw"
      }

      if ($destination.StartsWith('%USERPROFILE%/', [System.StringComparison]::OrdinalIgnoreCase)) {
        Add-DoctorError -Errors $Errors -Message "install manifest $scope destination 不能写入用户目录: $destinationRaw"
      }

      if ((Test-DoctorManifestPathRooted -Path $destinationRaw) -or (Test-DoctorManifestPathHasParentSegment -Path $destinationRaw)) {
        Add-DoctorError -Errors $Errors -Message "install manifest $scope destination 必须是项目内相对路径: $destinationRaw"
      }
    }

    $destinationKey = ($scope + "|" + $destination).ToLowerInvariant()
    if ($destinationKeys.ContainsKey($destinationKey)) {
      Add-DoctorError -Errors $Errors -Message "install manifest 同一 scope 下存在重复 destination: scope=$scope destination=$destination"
    }
    else {
      $destinationKeys[$destinationKey] = $true
    }

    $sourcePath = if ([string]::IsNullOrWhiteSpace($source)) { $null } else { Resolve-DoctorManifestSourcePath -Root $Root -ManifestDirectory $manifestDirectory -SourceRelativePath $source }
    if ($null -eq $sourcePath) {
      Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].source 指向不存在路径: $sourceRaw"
      continue
    }

    $sourceIsDirectory = Test-Path -LiteralPath $sourcePath -PathType Container
    if ($sourceIsDirectory) {
      if (-not $sourceRaw.Trim().Replace('\', '/').EndsWith('/', [System.StringComparison]::Ordinal)) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].source 指向目录时必须以 / 结尾: $sourceRaw"
      }

      if (-not $destinationRaw.Trim().Replace('\', '/').EndsWith('/', [System.StringComparison]::Ordinal)) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].destination 对应目录时必须以 / 结尾: $destinationRaw"
      }
    }
    else {
      if ($sourceRaw.Trim().Replace('\', '/').EndsWith('/', [System.StringComparison]::Ordinal)) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].source 指向文件时不能以 / 结尾: $sourceRaw"
      }

      if ($destinationRaw.Trim().Replace('\', '/').EndsWith('/', [System.StringComparison]::Ordinal)) {
        Add-DoctorError -Errors $Errors -Message "install manifest entries[$index].destination 对应文件时不能以 / 结尾: $destinationRaw"
      }
    }
  }

  foreach ($scopeName in $allowedScopes) {
    if ($scopeCounts[$scopeName] -eq 0) {
      if ($scopeName -eq "user") {
        Add-DoctorWarning -Warnings $Warnings -Message "install manifest 未声明 scope=user entries；当前实现依赖安装器单独处理用户级 package source。"
        continue
      }

      Add-DoctorError -Errors $Errors -Message "install manifest 缺少 scope=$scopeName 的 entries。"
    }
  }
}

function Test-ManifestDestinationCoversPath {
  param(
    [object[]]$Entries,
    [string]$RelativePath
  )

  $isRequiredDirectory = ([string]$RelativePath).Trim().Replace('\', '/').EndsWith('/', [System.StringComparison]::Ordinal)
  $required = Normalize-DoctorManifestPath -Path $RelativePath
  foreach ($entry in @($Entries)) {
    $destination = Normalize-DoctorManifestPath -Path ([string]$entry.destination)
    if ([string]::IsNullOrWhiteSpace($destination)) {
      continue
    }

    if ($destination -eq $required) {
      return $true
    }

    $destinationPrefix = $destination.TrimEnd('/') + '/'
    if ($required.StartsWith($destinationPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
      return $true
    }

    if ($isRequiredDirectory) {
      $requiredPrefix = $required.TrimEnd('/') + '/'
      if ($destination.StartsWith($requiredPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
        return $true
      }
    }
  }

  return $false
}

function Test-InstallManifestCoverage {
  param(
    [object]$Manifest,
    [ref]$Warnings
  )

  $entries = @($Manifest.entries)
  $requiredCoveragePaths = @(
    "AGENTS.md",
    "tools\harness\codex-loop.ps1",
    "tools\harness\doctor.ps1",
    ".codex-harness/manifests/install-manifest.json",
    ".codex/prompts/",
    ".codex/rules/",
    "docs/ai/",
    "docs/architecture/constraints.md",
    "docs/spec/",
    "docs/testing/",
    "docs/harness/",
    ".agents/skills/",
    ".agents/workflows/",
    ".agents/.specify/"
  )

  foreach ($relativePath in $requiredCoveragePaths) {
    if (-not (Test-ManifestDestinationCoversPath -Entries $entries -RelativePath $relativePath)) {
      Add-DoctorWarning -Warnings $Warnings -Message "install manifest 未覆盖关键安装路径: $relativePath"
    }
  }
}

function Get-HarnessConfigInfo {
  param([string]$Root)

  $configRelativePath = ".codex-harness\state\config.json"
  $configPath = Join-Path $Root $configRelativePath
  if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) {
    return [pscustomobject]@{
      Exists = $false
      Valid = $false
      InstallScope = ""
      Document = $null
    }
  }

  $configDocument = Read-JsonFileOrNull -Path $configPath
  return [pscustomobject]@{
    Exists = $true
    Valid = ($null -ne $configDocument)
    InstallScope = if ($null -ne $configDocument) { [string]$configDocument.installScope } else { "" }
    Document = $configDocument
  }
}

function Get-DoctorBaseProjectShape {
  param(
    [string]$Root,
    [object]$HarnessConfigInfo
  )

  if ($HarnessConfigInfo.Exists -and $HarnessConfigInfo.Valid) {
    $installScope = [string]$HarnessConfigInfo.InstallScope
    if ($installScope -eq "project") {
      return "thin-project"
    }

    if ($installScope -eq "vendor") {
      foreach ($legacyRootPath in @("codex-loop.ps1", "doctor.ps1", "verify.ps1", "env-check.ps1", "docs\harness", "docs\testing", "docs\knowledge", "docs\requirement-prep-kit", "skills", "workflows", ".specify", "scripts")) {
        if (Get-ExistingRelativePath -Root $Root -RelativePath $legacyRootPath) {
          return "legacy-full-project"
        }
      }

      return "vendor-project"
    }

    return "unknown-config"
  }

  $legacySignals = @(
    "docs\harness",
    "docs\testing",
    "docs\knowledge",
    "docs\requirement-prep-kit"
  )

  foreach ($relativePath in $legacySignals) {
    if ($relativePath -eq "docs\harness") {
      $harnessPath = Get-ExistingRelativePath -Root $Root -RelativePath $relativePath
      if ($harnessPath) {
        $allowedFiles = @("trace.schema.json")
        $allFiles = @(Get-ChildItem -LiteralPath $harnessPath -Recurse -File | ForEach-Object {
            $_.FullName.Substring($harnessPath.Length).TrimStart('\','/')
          })
        $allDirs = @(Get-ChildItem -LiteralPath $harnessPath -Recurse -Directory)
        $hasUnexpected = $allDirs.Count -gt 0
        foreach ($file in $allFiles) {
          if ($allowedFiles -notcontains $file.Replace('/','\')) {
            $hasUnexpected = $true
            break
          }
        }

        if ($hasUnexpected) {
          return "legacy-full-project"
        }
      }
      continue
    }

    if (Get-ExistingRelativePath -Root $Root -RelativePath $relativePath) {
      return "legacy-full-project"
    }
  }

  $agentsRoot = Get-ExistingRelativePath -Root $Root -RelativePath ".agents"
  if ($agentsRoot) {
    $allowedAgentEntries = @("skills", "workflows")
    foreach ($entry in Get-ChildItem -LiteralPath $agentsRoot -Force) {
      if ($allowedAgentEntries -notcontains $entry.Name) {
        return "legacy-full-project"
      }
    }
  }

  return "not-installed"
}

function Test-DoctorThinLegacyResidue {
  param([string]$Root)

  $legacyResiduePaths = @()

  foreach ($relativePath in $legacyResiduePaths) {
    if ($relativePath -eq "docs\harness") {
      $harnessPath = Get-ExistingRelativePath -Root $Root -RelativePath $relativePath
      if ($harnessPath) {
        $allowedFiles = @("trace.schema.json")
        $allFiles = @(Get-ChildItem -LiteralPath $harnessPath -Recurse -File | ForEach-Object {
            $_.FullName.Substring($harnessPath.Length).TrimStart('\','/')
          })
        $allDirs = @(Get-ChildItem -LiteralPath $harnessPath -Recurse -Directory)
        if ($allDirs.Count -gt 0) {
          return $true
        }

        foreach ($file in $allFiles) {
          if ($allowedFiles -notcontains $file.Replace('/','\')) {
            return $true
          }
        }
      }
      continue
    }

    if (Get-ExistingRelativePath -Root $Root -RelativePath $relativePath) {
      return $true
    }
  }

  $agentsRoot = Get-ExistingRelativePath -Root $Root -RelativePath ".agents"
  if ($agentsRoot) {
    $allowedAgentEntries = @("skills", "workflows")
    foreach ($entry in Get-ChildItem -LiteralPath $agentsRoot -Force) {
      if ($allowedAgentEntries -notcontains $entry.Name) {
        return $true
      }
    }
  }

  return $false
}

function Test-ProjectScopeInstallSurface {
  param(
    [string]$Root,
    [object]$ManifestSnapshot,
    [ref]$Errors
  )

  if ($null -eq $ManifestSnapshot) {
    return
  }

  $entries = @($ManifestSnapshot.entries | Where-Object { [string]$_.scope -eq "project" })
  foreach ($entry in $entries) {
    $destinationRaw = [string]$entry.destination
    if ([string]::IsNullOrWhiteSpace($destinationRaw)) {
      continue
    }

    $normalizedDestination = $destinationRaw.Replace('/', '\').TrimStart('\')
    $expectsDirectory = $destinationRaw.Trim().Replace('\', '/').EndsWith('/', [System.StringComparison]::Ordinal)
    $targetPath = Join-Path $Root $normalizedDestination

    if ($expectsDirectory) {
      if (-not (Test-Path -LiteralPath $targetPath -PathType Container)) {
        Add-DoctorError -Errors $Errors -Message "project scope 安装缺少目录: $normalizedDestination"
      }
      continue
    }

    if (-not (Test-Path -LiteralPath $targetPath -PathType Leaf)) {
      Add-DoctorError -Errors $Errors -Message "project scope 安装缺少文件: $normalizedDestination"
    }
  }
}

function Test-DoctorMissingUserPackage {
  param([object]$HarnessConfigInfo)

  if (-not ($HarnessConfigInfo.Exists -and $HarnessConfigInfo.Valid -and $HarnessConfigInfo.InstallScope -eq "project")) {
    return $false
  }

  $packageRoot = [string]$HarnessConfigInfo.Document.packageRoot
  if ([string]::IsNullOrWhiteSpace($packageRoot)) {
    return $true
  }

  if (-not (Test-Path -LiteralPath $packageRoot -PathType Container)) {
    return $true
  }

  $userPackageLock = Join-Path (Split-Path -Parent $packageRoot) "package.lock.json"
  if ($packageRoot -like "*\.codex\packages\codex-harness\*" -and -not (Test-Path -LiteralPath $userPackageLock -PathType Leaf)) {
    return $true
  }

  return $false
}

function Write-DoctorReadiness {
  param(
    [string]$Root,
    [string]$BaseProjectShape,
    [object]$HarnessConfigInfo,
    [int]$ErrorCount,
    [int]$WarningCount
  )

  $projectShape = $BaseProjectShape
  $readinessStatus = "PASS"
  $fixCommands = New-Object System.Collections.Generic.List[string]

  if ($BaseProjectShape -eq "not-installed") {
    $readinessStatus = "FAIL"
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -ProjectRoot . -Scope project")
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\harness\doctor.ps1")
  }
  elseif ($BaseProjectShape -eq "legacy-full-project") {
    $readinessStatus = "WARN"
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -ProjectRoot . -MigratePlan")
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -ProjectRoot . -MigrateApply -Confirm")
  }
  elseif ($BaseProjectShape -eq "vendor-project") {
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -ProjectRoot . -Scope vendor")
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\harness\doctor.ps1")
  }
  elseif ($BaseProjectShape -eq "thin-project") {
    $hasLegacyResidue = Test-DoctorThinLegacyResidue -Root $Root
    $missingUserPackage = Test-DoctorMissingUserPackage -HarnessConfigInfo $HarnessConfigInfo

    if ($missingUserPackage) {
      $projectShape = "thin-project-missing-user-package"
      $readinessStatus = "WARN"
      $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -Scope user")
      $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\harness\doctor.ps1")
    }
    elseif ($hasLegacyResidue) {
      $projectShape = "thin-project-with-legacy-residue"
      $readinessStatus = "WARN"
      $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -ProjectRoot . -MigratePlan")
    }
    else {
      $fixCommands.Add("none")
    }
  }
  else {
    $readinessStatus = "FAIL"
    $fixCommands.Add("powershell -NoProfile -ExecutionPolicy Bypass -File .\tools\install\install-agent.ps1 -ProjectRoot . -Scope project")
  }

  if ($ErrorCount -gt 0) {
    $readinessStatus = "FAIL"
  }
  elseif ($WarningCount -gt 0 -and $readinessStatus -eq "PASS" -and $BaseProjectShape -ne "vendor-project") {
    $readinessStatus = "WARN"
  }

  Write-Output ("Readiness gate: {0}" -f $readinessStatus)
  Write-Output ("ProjectShape: {0}" -f $projectShape)
  Write-Output "ReadinessChecks: read-only"
  foreach ($fixCommand in @($fixCommands.ToArray() | Select-Object -Unique)) {
    Write-Output ("FixCommand: {0}" -f $fixCommand)
  }
}

function Test-SpecManifest {
  param(
    [string]$Root,
    [ref]$Errors,
    [ref]$Warnings
  )

  $manifestRelativePath = "docs\\spec\\spec-manifest.json"
  $manifestPath = Join-Path $Root $manifestRelativePath
  if (-not (Test-Path -LiteralPath $manifestPath -PathType Leaf)) {
    Add-DoctorWarning -Warnings $Warnings -Message "缺少 Spec manifest: $manifestRelativePath"
    return
  }

  $manifest = Read-JsonFileOrNull -Path $manifestPath
  if ($null -eq $manifest) {
    Add-DoctorError -Errors $Errors -Message "spec-manifest.json 不是合法 JSON: $manifestRelativePath"
    return
  }

  $documents = @($manifest.documents)
  if ($documents.Count -lt 14) {
    Add-DoctorError -Errors $Errors -Message "spec-manifest.json documents 少于 14 项。"
  }

  $expectedIds = @("01","02","03","04","05","06","07","08","09","10","11","12","13","14")
  foreach ($expectedId in $expectedIds) {
    $entry = @($documents | Where-Object { [string]$_.id -eq $expectedId }) | Select-Object -First 1
    if ($null -eq $entry) {
      Add-DoctorError -Errors $Errors -Message "spec-manifest.json 缺少文档编号: $expectedId"
      continue
    }

    $entryPath = [string]$entry.path
    if ([string]::IsNullOrWhiteSpace($entryPath)) {
      Add-DoctorError -Errors $Errors -Message "spec-manifest.json 文档 $expectedId 缺少 path。"
      continue
    }

    if (-not (Get-ExistingRelativePath -Root $Root -RelativePath $entryPath)) {
      Add-DoctorError -Errors $Errors -Message "spec-manifest.json 文档 $expectedId 指向不存在文件: $entryPath"
    }
  }
}

function Test-PackageLock {
  param(
    [string]$Root,
    [ref]$Errors,
    [ref]$Warnings
  )

  $lockRelativePath = ".codex-harness\\locks\\package.lock.json"
  $lockPath = Join-Path $Root $lockRelativePath
  if (-not (Test-Path -LiteralPath $lockPath -PathType Leaf)) {
    Add-DoctorWarning -Warnings $Warnings -Message "缺少 package lock: $lockRelativePath"
    return
  }

  $lockDocument = Read-JsonFileOrNull -Path $lockPath
  if ($null -eq $lockDocument) {
    Add-DoctorError -Errors $Errors -Message "package.lock.json 不是合法 JSON: $lockRelativePath"
    return
  }

  if ([string]$lockDocument.package -ne "codex-harness") {
    Add-DoctorError -Errors $Errors -Message "package.lock.json package 必须是 codex-harness。"
  }

  if ([string]::IsNullOrWhiteSpace([string]$lockDocument.lockVersion)) {
    Add-DoctorError -Errors $Errors -Message "package.lock.json 缺少 lockVersion。"
  }

  if ([string]::IsNullOrWhiteSpace([string]$lockDocument.installMode)) {
    Add-DoctorError -Errors $Errors -Message "package.lock.json 缺少 installMode。"
  }

  if ([string]::IsNullOrWhiteSpace([string]$lockDocument.version)) {
    Add-DoctorWarning -Warnings $Warnings -Message "package.lock.json 缺少 version。"
  }

  if ($null -eq $lockDocument.PSObject.Properties["sourceCommit"]) {
    Add-DoctorWarning -Warnings $Warnings -Message "package.lock.json 缺少 sourceCommit。"
  }

  $managedFilesProperty = $lockDocument.PSObject.Properties["managedFiles"]
  if ($null -eq $managedFilesProperty) {
    Add-DoctorWarning -Warnings $Warnings -Message "package.lock.json 缺少 managedFiles，无法检查不可变安装文件漂移。"
    return
  }

  foreach ($managedFile in @($managedFilesProperty.Value)) {
    $relativePath = Normalize-DoctorManifestPath -Path ([string]$managedFile.path)
    if ([string]::IsNullOrWhiteSpace($relativePath)) {
      Add-DoctorError -Errors $Errors -Message "package.lock.json managedFiles 存在空 path。"
      continue
    }

    if ((Test-DoctorManifestPathRooted -Path $relativePath) -or (Test-DoctorManifestPathHasParentSegment -Path $relativePath)) {
      Add-DoctorError -Errors $Errors -Message "package.lock.json managedFiles path 必须是项目内相对路径: $relativePath"
      continue
    }

    $expectedSha256 = [string]$managedFile.sha256
    if ([string]::IsNullOrWhiteSpace($expectedSha256)) {
      Add-DoctorError -Errors $Errors -Message "package.lock.json managedFiles 缺少 sha256: $relativePath"
      continue
    }

    $fullPath = Join-Path $Root ($relativePath.Replace('/', '\'))
    if (-not (Test-Path -LiteralPath $fullPath -PathType Leaf)) {
      if ($relativePath -eq ".codex-harness/manifests/install-manifest.json") {
        Add-DoctorWarning -Warnings $Warnings -Message "install manifest snapshot 缺失: .codex-harness\manifests\install-manifest.json"
        continue
      }

      Add-DoctorError -Errors $Errors -Message "package.lock.json managed file 缺失: $relativePath"
      continue
    }

    $actualSha256 = Get-DoctorFileSha256 -Path $fullPath
    if ($actualSha256 -ne $expectedSha256.ToLowerInvariant()) {
      Add-DoctorError -Errors $Errors -Message "package.lock.json managed file drift: $relativePath"
    }
  }
}

function Test-InstallManifestSnapshot {
  param(
    [string]$Root,
    [bool]$RequireSnapshot,
    [ref]$Errors,
    [ref]$Warnings
  )

  $snapshotRelativePath = ".codex-harness\manifests\install-manifest.json"
  $snapshotPath = Join-Path $Root $snapshotRelativePath
  if (-not (Test-Path -LiteralPath $snapshotPath -PathType Leaf)) {
    if ($RequireSnapshot) {
      Add-DoctorWarning -Warnings $Warnings -Message "缺少 install manifest snapshot: $snapshotRelativePath"
    }
    return
  }

  $snapshot = Read-JsonFileOrNull -Path $snapshotPath
  if ($null -eq $snapshot) {
    Add-DoctorError -Errors $Errors -Message "install manifest snapshot 不是合法 JSON: $snapshotRelativePath"
    return
  }

  if ([string]$snapshot.package -ne "codex-harness") {
    Add-DoctorError -Errors $Errors -Message "install manifest snapshot package 必须是 codex-harness: $snapshotRelativePath"
  }

  if ([string]::IsNullOrWhiteSpace([string]$snapshot.version)) {
    Add-DoctorError -Errors $Errors -Message "install manifest snapshot 缺少 version: $snapshotRelativePath"
  }

  if (@($snapshot.installModes).Count -eq 0) {
    Add-DoctorError -Errors $Errors -Message "install manifest snapshot 缺少 installModes: $snapshotRelativePath"
  }

  if (@($snapshot.entries).Count -eq 0) {
    Add-DoctorError -Errors $Errors -Message "install manifest snapshot 缺少 entries: $snapshotRelativePath"
  }
}

function Test-ThinProjectSurface {
  param(
    [string]$Root,
    [ref]$Errors,
    [ref]$Warnings
  )

  $requiredThinProjectFiles = @(
    ".codex-harness\state\config.json",
    ".codex-harness\locks\package.lock.json",
    "docs\ai\repo-map.md",
    "docs\ai\architecture-brief.md",
    "docs\architecture\constraints.md",
    "docs\spec\01-spec-guide-and-index.md",
    "docs\spec\02-requirement-sources.md",
    "docs\spec\03-proposal-and-scope.md",
    "docs\spec\04-prd.md",
    "docs\spec\05-user-stories-and-acceptance.md",
    "docs\spec\06-functional-spec.md",
    "docs\spec\07-nfr.md",
    "docs\spec\08-architecture-design.md",
    "docs\spec\09-api-contract.md",
    "docs\spec\10-data-model.md",
    "docs\spec\11-security-design.md",
    "docs\spec\12-implementation-plan.md",
    "docs\spec\13-test-strategy.md",
    "docs\spec\14-rtm.md",
    "docs\spec\spec-manifest.json",
    "docs\spec\spec-manifest.schema.json"
  )

  foreach ($relativePath in $requiredThinProjectFiles) {
    if (-not (Get-ExistingRelativePath -Root $Root -RelativePath $relativePath)) {
      Add-DoctorError -Errors $Errors -Message "thin project 缺少必需文件: $relativePath"
    }
  }

  $legacyResiduePaths = @(
    "docs\requirement-prep-kit"
  )

  foreach ($relativePath in $legacyResiduePaths) {
    if ($relativePath -eq "docs\harness") {
      $harnessPath = Get-ExistingRelativePath -Root $Root -RelativePath $relativePath
      if ($harnessPath) {
        $allowedFiles = @("trace.schema.json")
        $allFiles = @(Get-ChildItem -LiteralPath $harnessPath -Recurse -File | ForEach-Object {
            $_.FullName.Substring($harnessPath.Length).TrimStart('\','/')
          })
        $allDirs = @(Get-ChildItem -LiteralPath $harnessPath -Recurse -Directory)
        $hasUnexpected = $allDirs.Count -gt 0
        foreach ($file in $allFiles) {
          if ($allowedFiles -notcontains $file.Replace('/','\')) {
            $hasUnexpected = $true
            break
          }
        }

        if ($hasUnexpected) {
          Add-DoctorWarning -Warnings $Warnings -Message "thin project 存在 legacy/vendor 残留: $relativePath"
        }
      }
      continue
    }

    if (Get-ExistingRelativePath -Root $Root -RelativePath $relativePath) {
      Add-DoctorWarning -Warnings $Warnings -Message "thin project 存在 legacy/vendor 残留: $relativePath"
    }
  }

  foreach ($requiredDir in @(".agents\skills", ".agents\workflows", ".codex-harness\state", ".codex-harness\locks", ".codex-harness\manifests", "tools\harness", "tools\install")) {
    if (-not (Get-ExistingRelativePath -Root $Root -RelativePath $requiredDir)) {
      Add-DoctorError -Errors $Errors -Message "thin project 缺少必需目录: $requiredDir"
    }
  }
}

function Read-InstallManifestSnapshotOrNull {
  param([string]$Root)

  $snapshotPath = Join-Path $Root ".codex-harness\manifests\install-manifest.json"
  if (-not (Test-Path -LiteralPath $snapshotPath -PathType Leaf)) {
    return $null
  }

  return Read-JsonFileOrNull -Path $snapshotPath
}

function Test-HarnessConfig {
  param(
    [string]$Root,
    [ref]$Errors,
    [ref]$Warnings
  )

  $configRelativePath = ".codex-harness\\state\\config.json"
  $configPath = Join-Path $Root $configRelativePath
  if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) {
    Add-DoctorWarning -Warnings $Warnings -Message "缺少 harness config: $configRelativePath"
    return
  }

  $configDocument = Read-JsonFileOrNull -Path $configPath
  if ($null -eq $configDocument) {
    Add-DoctorError -Errors $Errors -Message "config.json 不是合法 JSON: $configRelativePath"
    return
  }

  if ([string]$configDocument.package -ne "codex-harness") {
    Add-DoctorError -Errors $Errors -Message "config.json package 必须是 codex-harness。"
  }

  if ([string]::IsNullOrWhiteSpace([string]$configDocument.version)) {
    Add-DoctorWarning -Warnings $Warnings -Message "config.json 缺少 version。"
  }

  $installScope = [string]$configDocument.installScope
  if (@("project", "vendor") -notcontains $installScope) {
    Add-DoctorError -Errors $Errors -Message "config.json installScope 必须是 project 或 vendor。"
  }

  $packageRoot = [string]$configDocument.packageRoot
  if ([string]::IsNullOrWhiteSpace($packageRoot)) {
    Add-DoctorWarning -Warnings $Warnings -Message "config.json 缺少 packageRoot。"
  }
  elseif (-not (Test-Path -LiteralPath $packageRoot -PathType Container)) {
    Add-DoctorWarning -Warnings $Warnings -Message "config.json packageRoot 指向不存在目录: $packageRoot"
  }

  if ($installScope -eq "project" -and (Test-Path -LiteralPath $packageRoot -PathType Container)) {
    $userPackageLock = Join-Path (Split-Path -Parent $packageRoot) "package.lock.json"
    if ($packageRoot -like "*\.codex\packages\codex-harness\*" -and -not (Test-Path -LiteralPath $userPackageLock -PathType Leaf)) {
      Add-DoctorWarning -Warnings $Warnings -Message "用户级 package lock 缺失: $userPackageLock"
    }
  }
}

function Test-InstallManifest {
  param(
    [string]$Root,
    [ref]$Errors,
    [ref]$Warnings
  )

  $candidateRelativePaths = @(
    "install-manifest.json",
    ".agents\\install-manifest.json"
  )

  $manifestPath = $null
  $manifestRelativePath = ""
  foreach ($relativePath in $candidateRelativePaths) {
    $candidatePath = Join-Path $Root $relativePath
    if (Test-Path -LiteralPath $candidatePath -PathType Leaf) {
      $manifestPath = $candidatePath
      $manifestRelativePath = $relativePath
      break
    }
  }

  if ($null -eq $manifestPath) {
    Add-DoctorWarning -Warnings $Warnings -Message "缺少 install manifest: install-manifest.json 或 .agents\\install-manifest.json"
    return
  }

  $manifest = Read-JsonFileOrNull -Path $manifestPath
  if ($null -eq $manifest) {
    Add-DoctorError -Errors $Errors -Message "install manifest 不是合法 JSON: $manifestRelativePath"
    return
  }

  if ([string]$manifest.package -ne "codex-harness") {
    Add-DoctorError -Errors $Errors -Message "install manifest package 必须是 codex-harness: $manifestRelativePath"
  }

  if (@($manifest.installModes).Count -eq 0) {
    Add-DoctorError -Errors $Errors -Message "install manifest 缺少 installModes: $manifestRelativePath"
  }

  if (@($manifest.entries).Count -eq 0) {
    Add-DoctorError -Errors $Errors -Message "install manifest 缺少 entries: $manifestRelativePath"
  }

  $schemaCandidateRelativePaths = @(
    "install-manifest.schema.json",
    ".agents\\install-manifest.schema.json"
  )

  $schemaFound = $false
  foreach ($schemaRelativePath in $schemaCandidateRelativePaths) {
    $schemaPath = Join-Path $Root $schemaRelativePath
    if (Test-Path -LiteralPath $schemaPath -PathType Leaf) {
      $schemaFound = $true
      if (-not (Test-JsonFile -Path $schemaPath)) {
        Add-DoctorError -Errors $Errors -Message "install manifest schema 不是合法 JSON: $schemaRelativePath"
      }
      break
    }
  }

  if (-not $schemaFound) {
    Add-DoctorWarning -Warnings $Warnings -Message "缺少 install manifest schema: install-manifest.schema.json 或 .agents\\install-manifest.schema.json"
  }

  Test-InstallManifestGovernance `
    -Root $Root `
    -Manifest $manifest `
    -ManifestPath $manifestPath `
    -ManifestRelativePath $manifestRelativePath `
    -Errors $Errors `
    -Warnings $Warnings

  Test-InstallManifestCoverage -Manifest $manifest -Warnings $Warnings
}

$resolvedProjectRoot = (Resolve-Path -LiteralPath $ProjectRoot).Path
$taskPath = Join-Path $resolvedProjectRoot $TaskFile
$errors = @()
$warnings = @()
$harnessConfigInfo = Get-HarnessConfigInfo -Root $resolvedProjectRoot
$baseProjectShape = Get-DoctorBaseProjectShape -Root $resolvedProjectRoot -HarnessConfigInfo $harnessConfigInfo
$isThinProjectInstall = $harnessConfigInfo.Exists -and $harnessConfigInfo.Valid -and $harnessConfigInfo.InstallScope -eq "project"
$isVendorProjectInstall = $harnessConfigInfo.Exists -and $harnessConfigInfo.Valid -and $harnessConfigInfo.InstallScope -eq "vendor"
$isNotInstalledProject = $baseProjectShape -eq "not-installed"

$requiredRootFiles = @(
  "AGENTS.md",
  "tools\harness\codex-loop.ps1",
  "tools\harness\doctor.ps1",
  "tools\harness\task-structure-lint.ps1",
  "tools\harness\verify.ps1",
  "tools\install\env-check.ps1",
  "task.json",
  "docs\harness\trace.schema.json"
)

$requiredPromptFiles = @(
  ".codex\\prompts\\implement-one-task.md",
  ".codex\\prompts\\controller-loop.md",
  ".codex\\prompts\\review-stage1-spec.md",
  ".codex\\prompts\\review-stage2-quality.md",
  ".codex\\prompts\\visual-evaluator.md",
  ".codex\\prompts\\failure-triage.md",
  ".codex\\prompts\\repair-one-finding.md",
  ".codex\\prompts\\harness-audit.md",
  ".codex\\prompts\\worker-role\\frontend-worker.md",
  ".codex\\prompts\\worker-role\\backend-worker.md",
  ".codex\\prompts\\worker-role\\test-runner.md",
  ".codex\\prompts\\worker-role\\docs-worker.md"
)

$requiredHarnessDocs = @(
  "docs\\harness\\architecture.md",
  "docs\\harness\\install-manifest-governance.md",
  "docs\\harness\\new-project-usage.md",
  "docs\\harness\\regression-rules.md",
  "docs\\harness\\sandbox-policy.md",
  "docs\\harness\\spec-to-ui-to-code-workflow.md",
  "docs\\harness\\task-session-strategy.md",
  "docs\\harness\\trace-format.md"
)

$requiredFiles = @($requiredRootFiles + $requiredPromptFiles)
if ($isVendorProjectInstall -or $isNotInstalledProject) {
  $requiredFiles = @()
}
elseif (-not $isThinProjectInstall) {
  $requiredFiles += $requiredHarnessDocs
}

foreach ($relativePath in $requiredFiles) {
  if (-not (Get-ExistingRelativePath -Root $resolvedProjectRoot -RelativePath $relativePath)) {
    Add-DoctorError -Errors ([ref]$errors) -Message "缺少必需文件: $relativePath"
  }
}

$requiredSpecFiles = @(
  "docs\\spec\\01-spec-guide-and-index.md",
  "docs\\spec\\02-requirement-sources.md",
  "docs\\spec\\03-proposal-and-scope.md",
  "docs\\spec\\04-prd.md",
  "docs\\spec\\05-user-stories-and-acceptance.md",
  "docs\\spec\\06-functional-spec.md",
  "docs\\spec\\07-nfr.md",
  "docs\\spec\\08-architecture-design.md",
  "docs\\spec\\09-api-contract.md",
  "docs\\spec\\10-data-model.md",
  "docs\\spec\\11-security-design.md",
  "docs\\spec\\12-implementation-plan.md",
  "docs\\spec\\13-test-strategy.md",
  "docs\\spec\\14-rtm.md",
  "docs\\spec\\spec-manifest.json",
  "docs\\spec\\spec-manifest.schema.json"
)

if (-not $isVendorProjectInstall -and -not $isNotInstalledProject) {
  foreach ($relativePath in $requiredSpecFiles) {
    if (-not (Get-ExistingRelativePath -Root $resolvedProjectRoot -RelativePath $relativePath)) {
      if ($isThinProjectInstall) {
        Add-DoctorError -Errors ([ref]$errors) -Message "thin project 缺少 Spec 包文件: $relativePath"
      }
      else {
        Add-DoctorWarning -Warnings ([ref]$warnings) -Message "缺少 Spec 包文件: $relativePath"
      }
    }
  }
}

if ($isThinProjectInstall) {
  Test-ThinProjectSurface -Root $resolvedProjectRoot -Errors ([ref]$errors) -Warnings ([ref]$warnings)
}

$specManifestSchemaPath = Join-Path $resolvedProjectRoot "docs\\spec\\spec-manifest.schema.json"
if ((Test-Path -LiteralPath $specManifestSchemaPath) -and (-not (Test-JsonFile -Path $specManifestSchemaPath))) {
  Add-DoctorError -Errors ([ref]$errors) -Message "spec-manifest.schema.json 不是合法 JSON: docs\\spec\\spec-manifest.schema.json"
}

if ($isNotInstalledProject) {
  Add-DoctorError -Errors ([ref]$errors) -Message "readiness gate 未检测到 harness config 或 legacy vendor，请先安装 project scope。"
}
else {
  if (-not $isVendorProjectInstall) {
    Test-SpecManifest -Root $resolvedProjectRoot -Errors ([ref]$errors) -Warnings ([ref]$warnings)
  }

  Test-HarnessConfig -Root $resolvedProjectRoot -Errors ([ref]$errors) -Warnings ([ref]$warnings)
  Test-PackageLock -Root $resolvedProjectRoot -Errors ([ref]$errors) -Warnings ([ref]$warnings)
  if (-not $isThinProjectInstall) {
    Test-InstallManifest -Root $resolvedProjectRoot -Errors ([ref]$errors) -Warnings ([ref]$warnings)
  }
  Test-InstallManifestSnapshot -Root $resolvedProjectRoot -RequireSnapshot:$harnessConfigInfo.Exists -Errors ([ref]$errors) -Warnings ([ref]$warnings)
  if ($isThinProjectInstall) {
    $installManifestSnapshot = Read-InstallManifestSnapshotOrNull -Root $resolvedProjectRoot
    Test-ProjectScopeInstallSurface -Root $resolvedProjectRoot -ManifestSnapshot $installManifestSnapshot -Errors ([ref]$errors)
  }
}

if (-not (Test-Path -LiteralPath $taskPath)) {
  if ($isNotInstalledProject) {
    Add-DoctorWarning -Warnings ([ref]$warnings) -Message "缺少任务文件: $TaskFile"
  }
  elseif (-not $isVendorProjectInstall) {
    Add-DoctorError -Errors ([ref]$errors) -Message "缺少任务文件: $TaskFile"
  }
}
elseif (-not (Test-JsonFile -Path $taskPath)) {
  if (-not $isVendorProjectInstall) {
    Add-DoctorError -Errors ([ref]$errors) -Message "task.json 不是合法 JSON: $TaskFile"
  }
}
elseif (-not $isVendorProjectInstall) {
  $taskContent = Get-Content -LiteralPath $taskPath -Raw
  if (Test-TaskFileHasTemplatePlaceholder -Content $taskContent) {
    Add-DoctorError -Errors ([ref]$errors) -Message "task.json 仍包含模板占位符，请先替换为当前项目真实任务。"
  }

  $taskDocument = $taskContent | ConvertFrom-Json
  $runtime = $taskDocument.runtime
  if ($null -eq $runtime -or [string]::IsNullOrWhiteSpace([string]$runtime.driver)) {
    Add-DoctorError -Errors ([ref]$errors) -Message "task.json 缺少 runtime.driver"
  }
  else {
    $driverCommand = [string]$runtime.driver
    if ($driverCommand -match 'tools[\\/]+harness[\\/]+tools[\\/]+harness[\\/]+codex-loop\.ps1') {
      Add-DoctorError -Errors ([ref]$errors) -Message "task.json 的 runtime.driver 包含重复路径段 tools/harness/tools/harness，请改为 .\\tools\\harness\\codex-loop.ps1"
    }

    $driverFileMatch = [regex]::Match($driverCommand, '(?i)-File\s+("?)([^"]+)\1')
    if (-not $driverFileMatch.Success) {
      Add-DoctorError -Errors ([ref]$errors) -Message "task.json 的 runtime.driver 不是可解析的 PowerShell -File 命令: $driverCommand"
    }
    else {
      $driverRelativePath = $driverFileMatch.Groups[2].Value.Replace('/', '\')
      if ($driverRelativePath.StartsWith('.\')) {
        $driverRelativePath = $driverRelativePath.Substring(2)
      }

      $driverPath = Join-Path $resolvedProjectRoot $driverRelativePath
      if (-not (Test-Path -LiteralPath $driverPath)) {
        Add-DoctorError -Errors ([ref]$errors) -Message "task.json 的 runtime.driver 指向不存在的脚本: $driverRelativePath"
      }
    }
  }

  $runProfileRelativePath = $DefaultRunProfile
  if ($null -ne $runtime -and -not [string]::IsNullOrWhiteSpace($runtime.run_profile)) {
    $runProfileRelativePath = $runtime.run_profile
  }

  $runProfilePath = Join-Path $resolvedProjectRoot $runProfileRelativePath
  if (-not (Test-Path -LiteralPath $runProfilePath)) {
    Add-DoctorError -Errors ([ref]$errors) -Message "缺少 run profile: $runProfileRelativePath"
  }
  elseif (-not (Test-JsonFile -Path $runProfilePath)) {
    Add-DoctorError -Errors ([ref]$errors) -Message "run profile 不是合法 JSON: $runProfileRelativePath"
  }

  $allowedTaskKinds = @(
    "harness",
    "smoke",
    "feature_research",
    "feature_spec",
    "feature_design",
    "feature_plan",
    "testcase_design",
    "feature_impl",
    "release",
    "archive"
  )
  $allowedGateProfiles = @(
    "lightweight",
    "research_required",
    "spec_required",
    "contract_required",
    "release_required"
  )
  $allowedTruthSources = @(
    "repo_context",
    "research",
    "product",
    "design",
    "plan",
    "architecture",
    "testing",
    "contract",
    "knowledge"
  )
  $runtimeTruthSources = $null
  if ($null -ne $runtime -and $null -ne $runtime.PSObject.Properties["handoff"]) {
    $handoff = $runtime.handoff
    if ($null -ne $handoff -and $null -ne $handoff.PSObject.Properties["truth_sources"]) {
      $runtimeTruthSources = $handoff.truth_sources
    }
  }
  if ($null -ne $runtimeTruthSources) {
    foreach ($truthSourceProperty in $runtimeTruthSources.PSObject.Properties) {
      $truthSourceName = [string]$truthSourceProperty.Name
      if (-not [string]::IsNullOrWhiteSpace($truthSourceName) -and $allowedTruthSources -notcontains $truthSourceName) {
        $allowedTruthSources += $truthSourceName
      }
    }
  }

  $taskIds = @{}
  foreach ($task in @($taskDocument.tasks)) {
    if ($null -eq $task.id -or [string]::IsNullOrWhiteSpace([string]$task.id)) {
      Add-DoctorError -Errors ([ref]$errors) -Message "存在缺少 id 的任务。"
      continue
    }

    if ($taskIds.ContainsKey($task.id)) {
      Add-DoctorError -Errors ([ref]$errors) -Message "存在重复 task id: $($task.id)"
    }
    else {
      $taskIds[$task.id] = $true
    }

    $taskKindProperty = $task.PSObject.Properties["task_kind"]
    $taskKind = if ($null -ne $taskKindProperty) { [string]$taskKindProperty.Value } else { "" }
    if (-not [string]::IsNullOrWhiteSpace($taskKind) -and $allowedTaskKinds -notcontains $taskKind) {
      Add-DoctorError -Errors ([ref]$errors) -Message "任务 $($task.id) 使用了未知 task_kind: $taskKind"
    }

    $gateProfileProperty = $task.PSObject.Properties["gate_profile"]
    $gateProfile = if ($null -ne $gateProfileProperty) { [string]$gateProfileProperty.Value } else { "" }
    if (-not [string]::IsNullOrWhiteSpace($gateProfile) -and $allowedGateProfiles -notcontains $gateProfile) {
      Add-DoctorError -Errors ([ref]$errors) -Message "任务 $($task.id) 使用了未知 gate_profile: $gateProfile"
    }

    $requiredTruthSourcesProperty = $task.PSObject.Properties["required_truth_sources"]
    $requiredTruthSources = if ($null -ne $requiredTruthSourcesProperty) { $requiredTruthSourcesProperty.Value } else { @() }
    foreach ($truthSource in @($requiredTruthSources)) {
      if ($allowedTruthSources -notcontains [string]$truthSource) {
        Add-DoctorError -Errors ([ref]$errors) -Message "任务 $($task.id) 使用了未知 required_truth_source: $truthSource"
      }
    }
  }

  Invoke-TaskStructureValidation -Root $resolvedProjectRoot -Errors ([ref]$errors) -Warnings ([ref]$warnings)
}

Write-DoctorReadiness `
  -Root $resolvedProjectRoot `
  -BaseProjectShape $baseProjectShape `
  -HarnessConfigInfo $harnessConfigInfo `
  -ErrorCount $errors.Count `
  -WarningCount $warnings.Count

if ($errors.Count -gt 0) {
  Write-Output "Runtime doctor failed."
  foreach ($errorMessage in $errors) {
    Write-Output "- $errorMessage"
  }
  exit 1
}

if ($warnings.Count -gt 0) {
  Write-Output "Runtime doctor passed with warnings."
  foreach ($warningMessage in $warnings) {
    Write-Output "- $warningMessage"
  }
  exit 0
}

Write-Output "Runtime doctor passed."
