From bac67a54051d9cbea429dd29e49249241a86797a Mon Sep 17 00:00:00 2001 From: JOY Date: Sat, 23 May 2026 18:23:18 +0700 Subject: [PATCH] chore: improve local health check guidance --- CHANGELOG.md | 3 ++ docs/setup/agent-handoff.md | 5 ++- docs/setup/play-mode-smoke-checklist.md | 5 +++ tools/windows/check-local-health.ps1 | 60 +++++++++++++++---------- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eebb3ac..5108a98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,6 +124,9 @@ versioned release tag yet, so entries are organized as pre-alpha snapshots. - Local Windows health check helper for Git alignment, Docker, Nakama containers, Nakama HTTP, Unity process ownership, recent Editor.log compile/package patterns, and Unity MCP port readiness. +- Local Windows health check reports now include a summary verdict and + per-component next-action hints, and they filter empty AI decision `error=` + fields from the generic Unity log warning path. - Play Mode smoke checklist for NPC dialogue, ambient society behavior, quest tracker, memory and relationship writes, PromptTrace, fallback display, combat safety, MCP, Unity console, and backend evidence. diff --git a/docs/setup/agent-handoff.md b/docs/setup/agent-handoff.md index 8713684..1a4ac41 100644 --- a/docs/setup/agent-handoff.md +++ b/docs/setup/agent-handoff.md @@ -105,8 +105,9 @@ Do not touch: `tools/windows/check-local-health.ps1`. It reports Git alignment, Docker, `second-spawn-postgres`, `second-spawn-nakama`, Nakama HTTP, Unity process, recent Editor.log compile/package patterns, and Unity MCP ports without - killing or restarting anything. Use `-Strict` only when an automation should - fail on hard failures. + killing or restarting anything. The report includes a summary verdict and + per-component next-action hints so agents can fix the failing lane instead of + guessing. Use `-Strict` only when an automation should fail on hard failures. - Play Mode verification uses [Play Mode Smoke Checklist](play-mode-smoke-checklist.md) for NPC dialogue, quest tracker, PromptTrace, memory, relationship, fallback display, combat diff --git a/docs/setup/play-mode-smoke-checklist.md b/docs/setup/play-mode-smoke-checklist.md index 9471e9d..c1b06bf 100644 --- a/docs/setup/play-mode-smoke-checklist.md +++ b/docs/setup/play-mode-smoke-checklist.md @@ -24,6 +24,11 @@ Run the local health check first: powershell -NoProfile -ExecutionPolicy Bypass -File tools/windows/check-local-health.ps1 ``` +Use `-Json` when an agent or CI-style helper needs machine-readable evidence. +The JSON output includes a summary verdict and per-component `next_action` +guidance. `WARN` means Play Mode can often continue with a note, while `FAIL` +means fix the named component before claiming the smoke is clean. + When a previous Play Mode run just stopped, wait at least 10-15 seconds before clearing the Unity console and starting the next smoke. Unity `6000.5.0b9` can finish async domain-reload cleanup after the stop command returns, and clearing diff --git a/tools/windows/check-local-health.ps1 b/tools/windows/check-local-health.ps1 index 5977d18..df35a6f 100644 --- a/tools/windows/check-local-health.ps1 +++ b/tools/windows/check-local-health.ps1 @@ -31,7 +31,8 @@ function Add-Result { [string]$Component, [Parameter(Mandatory = $true)] [string]$Message, - [string]$Detail = "" + [string]$Detail = "", + [string]$NextAction = "" ) $script:results.Add([PSCustomObject]@{ @@ -39,6 +40,7 @@ function Add-Result { component = $Component message = $Message detail = $Detail + next_action = $NextAction }) } @@ -170,7 +172,7 @@ if (Test-Path $UnityProjectPath) { Add-Result -Status "OK" -Component "Unity Project" -Message "Unity project folder exists." -Detail $UnityProjectPath } else { - Add-Result -Status "FAIL" -Component "Unity Project" -Message "Unity project folder is missing." -Detail $UnityProjectPath + Add-Result -Status "FAIL" -Component "Unity Project" -Message "Unity project folder is missing." -Detail $UnityProjectPath -NextAction "Open the repository root or pass -UnityProjectPath." } if (Test-CommandAvailable "git") { @@ -183,15 +185,15 @@ if (Test-CommandAvailable "git") { Add-Result -Status "OK" -Component "Git" -Message "Branch is aligned with upstream." -Detail $detail } else { - Add-Result -Status "WARN" -Component "Git" -Message "Branch is not aligned with upstream." -Detail $detail + Add-Result -Status "WARN" -Component "Git" -Message "Branch is not aligned with upstream." -Detail $detail -NextAction "Fetch and rebase or fast-forward before starting new work." } } else { - Add-Result -Status "WARN" -Component "Git" -Message "No upstream branch detected." -Detail "branch=$branch" + Add-Result -Status "WARN" -Component "Git" -Message "No upstream branch detected." -Detail "branch=$branch" -NextAction "Set an upstream branch or use a repo worktree created from origin/dev." } } else { - Add-Result -Status "WARN" -Component "Git" -Message "Git CLI not found." + Add-Result -Status "WARN" -Component "Git" -Message "Git CLI not found." -NextAction "Install Git or run this from a shell where Git is on PATH." } if (Test-CommandAvailable "docker") { @@ -201,27 +203,27 @@ if (Test-CommandAvailable "docker") { Add-Result -Status "OK" -Component "Docker" -Message "Docker engine is reachable." } else { - Add-Result -Status "FAIL" -Component "Docker" -Message "Docker engine is not reachable." + Add-Result -Status "FAIL" -Component "Docker" -Message "Docker engine is not reachable." -NextAction "Start Docker Desktop, then rerun tools/windows/start-local-nakama.ps1 if containers are stopped." } } catch { - Add-Result -Status "FAIL" -Component "Docker" -Message "Docker engine is not reachable." -Detail $_.Exception.Message + Add-Result -Status "FAIL" -Component "Docker" -Message "Docker engine is not reachable." -Detail $_.Exception.Message -NextAction "Start Docker Desktop, then rerun tools/windows/start-local-nakama.ps1 if containers are stopped." } } else { - Add-Result -Status "FAIL" -Component "Docker" -Message "Docker CLI not found." + Add-Result -Status "FAIL" -Component "Docker" -Message "Docker CLI not found." -NextAction "Install Docker Desktop or open a shell where Docker is on PATH." } foreach ($containerName in @("second-spawn-postgres", "second-spawn-nakama")) { $state = Get-ContainerState -Name $containerName if (-not $state.exists) { - Add-Result -Status "FAIL" -Component "Docker Container" -Message "$containerName is missing." -Detail $state.detail + Add-Result -Status "FAIL" -Component "Docker Container" -Message "$containerName is missing." -Detail $state.detail -NextAction "Run tools/windows/start-local-nakama.ps1 to create the local backend containers." } elseif ($state.running) { Add-Result -Status "OK" -Component "Docker Container" -Message "$containerName is running." -Detail "restart=$($state.restartPolicy) status=$($state.detail)" } else { - Add-Result -Status "FAIL" -Component "Docker Container" -Message "$containerName is not running." -Detail "restart=$($state.restartPolicy) status=$($state.detail)" + Add-Result -Status "FAIL" -Component "Docker Container" -Message "$containerName is not running." -Detail "restart=$($state.restartPolicy) status=$($state.detail)" -NextAction "Run tools/windows/start-local-nakama.ps1 or docker start $containerName." } } @@ -231,14 +233,14 @@ try { $nakamaPort = if ($nakamaUri.Port -gt 0) { $nakamaUri.Port } else { 7350 } } catch { - Add-Result -Status "WARN" -Component "Nakama" -Message "Nakama URL is not a valid URI." -Detail $NakamaUrl + Add-Result -Status "WARN" -Component "Nakama" -Message "Nakama URL is not a valid URI." -Detail $NakamaUrl -NextAction "Pass a valid -NakamaUrl, for example http://127.0.0.1:7350." } if (Test-TcpPort -HostName "127.0.0.1" -Port $nakamaPort) { Add-Result -Status "OK" -Component "Nakama" -Message "Nakama TCP port is open." -Detail $NakamaUrl } else { - Add-Result -Status "FAIL" -Component "Nakama" -Message "Nakama TCP port is closed." -Detail $NakamaUrl + Add-Result -Status "FAIL" -Component "Nakama" -Message "Nakama TCP port is closed." -Detail $NakamaUrl -NextAction "Start the local Nakama container and confirm port 7350 is not occupied by another service." } $nakamaHttp = Test-Http -Url $NakamaUrl @@ -246,15 +248,15 @@ if ($nakamaHttp.ok) { Add-Result -Status "OK" -Component "Nakama HTTP" -Message "Nakama HTTP endpoint responded." -Detail "http=$($nakamaHttp.code) $($nakamaHttp.detail)" } elseif ($nakamaHttp.code -ge 400 -and $nakamaHttp.code -lt 500) { - Add-Result -Status "WARN" -Component "Nakama HTTP" -Message "Nakama HTTP endpoint responded with client status." -Detail "http=$($nakamaHttp.code) $($nakamaHttp.detail)" + Add-Result -Status "WARN" -Component "Nakama HTTP" -Message "Nakama HTTP endpoint responded with client status." -Detail "http=$($nakamaHttp.code) $($nakamaHttp.detail)" -NextAction "Confirm the URL points at Nakama and not the console or another service." } else { - Add-Result -Status "FAIL" -Component "Nakama HTTP" -Message "Nakama HTTP endpoint did not respond cleanly." -Detail $nakamaHttp.detail + Add-Result -Status "FAIL" -Component "Nakama HTTP" -Message "Nakama HTTP endpoint did not respond cleanly." -Detail $nakamaHttp.detail -NextAction "Check second-spawn-nakama logs and restart the local backend if needed." } $unityProcesses = @(Get-UnityProcesses) if ($unityProcesses.Count -eq 0) { - Add-Result -Status "WARN" -Component "Unity Editor" -Message "No Unity.exe process detected." + Add-Result -Status "WARN" -Component "Unity Editor" -Message "No Unity.exe process detected." -NextAction "Open Unity with the Unity project before Play Mode smoke." } else { $matching = $unityProcesses | Where-Object { $_.CommandLine -like "*$UnityProjectPath*" } @@ -262,44 +264,45 @@ else { Add-Result -Status "OK" -Component "Unity Editor" -Message "Unity is open for this project." -Detail (($matching | ForEach-Object { "pid=$($_.ProcessId)" }) -join ", ") } else { - Add-Result -Status "WARN" -Component "Unity Editor" -Message "Unity is running, but not clearly for this project." -Detail (($unityProcesses | ForEach-Object { "pid=$($_.ProcessId)" }) -join ", ") + Add-Result -Status "WARN" -Component "Unity Editor" -Message "Unity is running, but not clearly for this project." -Detail (($unityProcesses | ForEach-Object { "pid=$($_.ProcessId)" }) -join ", ") -NextAction "Focus the SECOND SPAWN Unity project or pass -UnityProjectPath." } } $logTail = @(Read-EditorLogTail -Path $EditorLogPath -TailLines $LogTailLines) if ($logTail.Count -eq 0) { - Add-Result -Status "WARN" -Component "Unity Editor Log" -Message "Editor log was not found or is empty." -Detail $EditorLogPath + Add-Result -Status "WARN" -Component "Unity Editor Log" -Message "Editor log was not found or is empty." -Detail $EditorLogPath -NextAction "Open Unity once so Editor.log exists, or pass -EditorLogPath." } else { $packageManagerMatches = @($logTail | Select-String -Pattern 'The "path" argument must be of type string|Failed to resolve packages|Package Manager' -SimpleMatch:$false) - $compileMatches = @($logTail | Select-String -Pattern "Assets\\.*\\.cs\\(.*\\): error CS|Compilation failed|Compiler errors") + $compileMatches = @($logTail | Select-String -Pattern "Assets[\\/].*\.cs\([0-9]+,[0-9]+\): error CS|Compilation failed|Compiler errors") $errorMatches = @($logTail | Select-String -Pattern "Exception|Error|Failed" | Where-Object { $line = $_.Line.Trim() $line -notmatch "The referenced script .* is missing" -and $line -notmatch "System\.Exception" -and $line -notmatch "^at " -and $line -notmatch "^[A-Za-z0-9_.<>/]+:.*\(.*\)\s+\(at " -and + $line -notmatch "^\[PrototypeAgentBrain\] Decision result .* error=\s*$" -and $line -notmatch "Unity\.AI\.Tracing\.(ConsoleSink|TraceLogger|TraceWriter)" -and $line -notmatch "Unity\.AI\.MCP\.Editor\.Helpers\.McpLog" }) $warningMatches = @($logTail | Select-String -Pattern "Warning|WARN") if ($compileMatches.Count -gt 0) { - Add-Result -Status "FAIL" -Component "Unity Compile" -Message "Compile errors found in recent Editor.log." -Detail (($compileMatches | Select-Object -Last 3 | ForEach-Object { $_.Line.Trim() }) -join " | ") + Add-Result -Status "FAIL" -Component "Unity Compile" -Message "Compile errors found in recent Editor.log." -Detail (($compileMatches | Select-Object -Last 3 | ForEach-Object { $_.Line.Trim() }) -join " | ") -NextAction "Fix the reported C# compile errors before entering Play Mode." } else { Add-Result -Status "OK" -Component "Unity Compile" -Message "No compile error pattern found in recent Editor.log." } if ($packageManagerMatches.Count -gt 0) { - Add-Result -Status "WARN" -Component "Unity Package Manager" -Message "Package Manager warning pattern found in recent Editor.log." -Detail (($packageManagerMatches | Select-Object -Last 3 | ForEach-Object { $_.Line.Trim() }) -join " | ") + Add-Result -Status "WARN" -Component "Unity Package Manager" -Message "Package Manager warning pattern found in recent Editor.log." -Detail (($packageManagerMatches | Select-Object -Last 3 | ForEach-Object { $_.Line.Trim() }) -join " | ") -NextAction "Open Package Manager or restart Unity if it is stuck resolving packages." } else { Add-Result -Status "OK" -Component "Unity Package Manager" -Message "No known package blocker pattern found in recent Editor.log." } if ($errorMatches.Count -gt 0) { - Add-Result -Status "WARN" -Component "Unity Editor Log" -Message "Recent log contains error or exception text." -Detail (($errorMatches | Select-Object -Last 3 | ForEach-Object { $_.Line.Trim() }) -join " | ") + Add-Result -Status "WARN" -Component "Unity Editor Log" -Message "Recent log contains error or exception text." -Detail (($errorMatches | Select-Object -Last 3 | ForEach-Object { $_.Line.Trim() }) -join " | ") -NextAction "Inspect the recent Unity console. Compile errors are already reported separately as FAIL." } else { Add-Result -Status "OK" -Component "Unity Editor Log" -Message "No generic error or exception text found in recent log tail." @@ -315,16 +318,26 @@ foreach ($port in $McpPorts) { Add-Result -Status "OK" -Component "Unity MCP" -Message "Port $port is listening." -Detail $detail } else { - Add-Result -Status "WARN" -Component "Unity MCP" -Message "Port $port is not listening." + Add-Result -Status "WARN" -Component "Unity MCP" -Message "Port $port is not listening." -NextAction "Start or re-enable the matching Unity MCP bridge only if this bridge is needed for the smoke." } } +$failCount = @($results | Where-Object { $_.status -eq "FAIL" }).Count +$warnCount = @($results | Where-Object { $_.status -eq "WARN" }).Count +$okCount = @($results | Where-Object { $_.status -eq "OK" }).Count + $summary = [PSCustomObject]@{ generated_at = (Get-Date).ToString("o") repo_root = $RepoRoot unity_project_path = $UnityProjectPath nakama_url = $NakamaUrl strict = [bool]$Strict + summary = [PSCustomObject]@{ + ok = $okCount + warn = $warnCount + fail = $failCount + verdict = if ($failCount -gt 0) { "fail" } elseif ($warnCount -gt 0) { "warn" } else { "ok" } + } results = $results } @@ -337,8 +350,9 @@ else { Write-Host "Repo: $RepoRoot" Write-Host "Unity: $UnityProjectPath" Write-Host "Nakama: $NakamaUrl" + Write-Host "Verdict: $($summary.summary.verdict.ToUpperInvariant()) ($okCount OK, $warnCount WARN, $failCount FAIL)" Write-Host "" - $results | Format-Table status, component, message, detail -AutoSize + $results | Format-Table status, component, message, detail, next_action -AutoSize } $hasFailure = [bool]($results | Where-Object { $_.status -eq "FAIL" })