Skip to content

feat(deploy): Mode C 一鍵部屬 hybrid orchestrator(deploy.ps1 + scripts\lib\* + tests) - #124

Merged
monkey1sai merged 26 commits into
mainfrom
docs/one-click-deploy-design-2026-05-26
May 26, 2026
Merged

monkey1sai merged 26 commits into
mainfrom
docs/one-click-deploy-design-2026-05-26

Conversation

@monkey1sai

@monkey1sai monkey1sai commented May 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

新建 scripts\deploy.ps1 + scripts\lib\*.ps1(8 個 module)+ scripts\tests\test-*.ps1(9 個 test file),讓 Mode C(web-plane Docker + host-native Kit)一條指令冷啟到 demo-ready。完全不動 start-all.ps1 / start-web-plane-docker.ps1 / start-runtime-manager-docker.ps1 / compose.*.yml(spec §4 / §13 acceptance #10)。

Spec / Plan:

  • docs/superpowers/specs/2026-05-26-one-click-deploy-design.md (commit c900bab)
  • docs/superpowers/plans/2026-05-26-one-click-deploy.md (commit c45d27e)

架構

.\scripts\deploy.ps1 (薄 orchestrator,~490 行)
  → Phase 1 preflight(read-only):docker / host-native / .env / ports / volume alignment
  → Phase 2 auto-fix(安全項目):venv / .env missing-key merge / volume alignment append /
                              stale PID 清 / 建目錄 / 容器衝突 rm / 第一次 docker build
  → Phase 3 interactive guard(動到別人活著 process 才問):陌生 PID 佔 port / venv WRONG_VERSION
  → Phase 4 嚴格順序啟動:4a conversion → 4b Kit → 4c docker compose up
  → Phase 5 post-start verify(best-effort)
  → Final Summary(成功 / 失敗各印對應 next steps)

8 個 lib module:deploy-report / preflight-docker / preflight-host-native / preflight-env / preflight-ports / preflight-volume-alignment / host-native-launcher / kit-log-probe。

退出碼

0 / 1 / 2 / 3 / 4 / 5(對齊 spec §6.3,Phase 4 內部 stage 在 log 標 stage=4a/4b/4c)。

實機驗證(本機 Windows + NVIDIA GPU)

Step Status Detail
1 Cold start ✅ stop-all + 砍 .venv / node_modules / .env.web-plane.host-kit + docker compose down -v
2 First deploy ✅ 1m 42s 全綠,Phase 1-5 全 [ok],Kit 抓 app ready,verify 全 200
3 Coordinator UI(瀏覽器目視) ⏸️ 留 PR review 階段補驗
4 Viewer WebRTC 畫面 ⏸️ 同上(需 readyState=4 + 影像 > 0)
5 Idempotent re-run ✅ 5s,Phase 2 全 [skip],Phase 4 全 [skip],verify 全 200
6 -Build force rebuild ⏸️ 留 PR review 補驗
7 Fail injection(關 Docker Desktop) ⏸️ 需 user 手動操作 Docker tray,留 PR review

蓋章紀錄見 docs/runbooks/one-click-deploy-smoke.md Smoke Pass Log。

Layer 1 unit tests + Layer 2 integration

9 個 test file 全綠(repo 風格:純 PowerShell + Assert-* helpers,不引入 Pester):

test-helpers / test-deploy-report / test-preflight-docker / test-preflight-host-native /
test-preflight-env / test-preflight-ports / test-preflight-volume-alignment /
test-host-native-launcher / test-kit-log-probe / test-deploy-dryrun

GitNexus impact

跑 npx gitnexus analyze --embeddings 重 index 後:

  • nodes:5,042 → 5,260(+218 新 PowerShell symbols)
  • edges:9,201 → 9,419(+218)
  • detect_changes scope=compare base=main:
    • risk_level: low
    • affected_processes: 0(沒觸動任何既有 execution flow)

Scope

git diff --name-only main...HEAD(22 個檔):

docs/runbooks/one-click-deploy-smoke.md          (NEW)
docs/superpowers/plans/2026-05-26-one-click-deploy.md   (NEW)
docs/superpowers/specs/2026-05-26-one-click-deploy-design.md  (NEW)
scripts/deploy.ps1                               (NEW)
scripts/lib/{deploy-report,preflight-docker,preflight-host-native,preflight-env,
              preflight-ports,preflight-volume-alignment,host-native-launcher,
              kit-log-probe}.ps1                 (NEW × 8)
scripts/tests/{test-helpers,test-deploy-report,test-preflight-docker,
                test-preflight-host-native,test-preflight-env,test-preflight-ports,
                test-preflight-volume-alignment,test-host-native-launcher,
                test-kit-log-probe,test-deploy-dryrun}.ps1   (NEW × 10)

start-all.ps1 / start-runtime-manager-docker.ps1 / start-web-plane-docker.ps1 / compose.*.yml 完全沒動。

Out of scope(留 v2 / follow-up PR)

  • CI workflow yml(.github/workflows/deploy-unit-test.yml)
  • Mode A / Mode B 一鍵部屬
  • Production 部屬(Service / Autostart)
  • Phase 1 對 Docker forwarder(wslrelay / com.docker.backend)印 [ask] 後 Phase 3 才 whitelist 過濾 — cosmetic,可改成 Phase 1 直接 [skip]

已知 fix iterations(實機暴露的 PowerShell trap)

  1. $pid automatic variable(param($pid) 不 bind):rename $procId
  2. $Args automatic variable:rename $ArgList
  3. Assert-Equal $null Expected:用 Assert-True ($null -eq …) 寫法
  4. [string] $param = \$null cast 成 empty string:用 IsNullOrEmpty 判斷
  5. PowerShell 5.1 \$ErrorActionPreference='Stop' + native cmd stderr promotion:改 Continue
  6. Write-Host 走 Information stream:test 用 *>&1 抓所有 stream
  7. 子 script stderr 污染父流程:Start-Process 隔離 PowerShell process
  8. Kit log 關鍵字 app ready(小寫):加入 keyword list
  9. wrapper PID file 不含 child kit.exe / python.exe:Get-PidsFromRunDir 沿 ParentProcessId 遞迴展開子孫
  10. Get-NetTCPConnection.OwningProcess UInt32 vs hashtable Int32 key type mismatch:統一 [int] cast

Test plan(reviewer 補驗)

  • Step 3 開 http://127.0.0.1:8004/ui 確認 coordinator UI 載入
  • Step 4 開 http://127.0.0.1:5173 確認 viewer WebRTC handshake 有畫面(readyState=4 + 影像尺寸 > 0)
  • Step 6 .\scripts\deploy.ps1 -Build 確認 force rebuild
  • Step 7 關 Docker Desktop 後 .\scripts\deploy.ps1 確認 Phase 1 [fail] 退 1
  • 驗 start-all.ps1 / start-web-plane-docker.ps1 / start-runtime-manager-docker.ps1 / compose.*.yml 完全沒動(git diff main...HEAD --stat 不含這些路徑)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • One-click deployment automation with preflight environment validation and health verification
    • Automatic correction of missing configurations and stale resources
    • Coordinated multi-phase service startup orchestration
    • Comprehensive deployment testing framework with validation tools
  • Documentation

    • New smoke test runbook documenting deployment validation procedures
    • Complete deployment architecture and implementation specification

Review Change Stack

monkey1sai and others added 24 commits May 26, 2026 15:07
從 /superpowers:brainstorming 收斂出 deploy.ps1(Mode C hybrid) 的設計:
preflight + auto-fix + 依賴順序啟 host-native conversion → Kit → docker
compose,完成後印可診斷 summary。包含 module 分工、退出碼語意、A/B/C
三層 safety 紅線、Volume 對齊方案 A、三層測試策略與 acceptance criteria。

下一步:由 writing-plans 接力產出 implementation plan。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
從 /superpowers:writing-plans 接力 design spec(c900bab)收斂出 13 個
bite-sized task,每 task TDD 一 module:

  Task 1  scripts\tests\test-helpers.ps1 共用 assert helpers
  Task 2  lib\deploy-report.ps1 + test
  Task 3  lib\preflight-docker.ps1 + test
  Task 4  lib\preflight-host-native.ps1 + test
  Task 5  lib\preflight-env.ps1 + test
  Task 6  lib\preflight-ports.ps1 + test
  Task 7  lib\preflight-volume-alignment.ps1 + test
  Task 8  lib\host-native-launcher.ps1 + test
  Task 9  lib\kit-log-probe.ps1 + test
  Task 10 scripts\deploy.ps1 orchestrator(整合所有 lib + Phase 0-5)
  Task 11 scripts\tests\test-deploy-dryrun.ps1 integration
  Task 12 docs\runbooks\one-click-deploy-smoke.md Layer 3 smoke
  Task 13 GitNexus re-index + PR

每個 task 內含完整 PowerShell code(無 placeholder)、test cases、commit
指令。Self-Review 已通過 spec coverage + placeholder scan + type
consistency 三項。

同時微調 spec §9.1 / §9.2 把「Pester」字眼改為「repo 風格(純
PowerShell + Assert-* helper)」:repo Pester 是 3.4.0 老版,實際既有
測試(test-pr-review-agent.ps1)都用純 PowerShell + assert helpers。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Repo 沿用 test-pr-review-agent.ps1 純 PowerShell 測試風格(不引入 Pester)。
這個檔把 Assert-True / Assert-Equal / Assert-Throws / New-TestSandbox /
Remove-TestSandbox / Write-TestPass / Write-TestFail 抽出共用,供
deploy.ps1 系列 module 的 test scripts dot-source 使用。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Write-DeployTag(-Tag ok|fix|ask|skip|warn|fail -Message X -LogPath Y) 統一
deploy.ps1 / scripts\lib\* 的輸出格式。同步寫進 deploy.log 與 stdout,
供 Final Summary 連回。Write-DeployHeader 印階段分隔。

對應 spec §8.1 (Output Format) 與 §8.3 (落地物 deploy.log)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test-DockerEnvironment 回傳結構化 audit:cliVersion / composeV2 /
engineRunning / envFile / ok。所有外部 CLI 都透過可注入 scriptblock
參數,test 內 fake 各種狀態(docker missing / engine down / .env
fallback)。

對應 spec §5.2 module list 與 §6.2 audit result 的 docker 區塊。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test-HostNativeEnvironment 偵測 .venv (OK | MISSING | WRONG_VERSION)、
Kit launcher path (OK | MISSING_PATH)、nvidia-smi (OK | MISSING)。
Python probe / NvidiaSmi probe 都接 scriptblock 注入。

對應 spec §5.2 與 §6.2 hostNative 區塊。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Get-EnvKeyList / Get-EnvAudit / Test-EnvFiles 對三個目標檔(root .env、
bim-review-coordinator/.env、.env.web-plane.host-kit)各自跟對應 .example
比對 missing key list。Invariant:.env 已有的 key 一律不出現在 missing
(Phase 2 才會 append 預設值,不覆寫實值,符合 spec §7.3 紅線)。

對應 spec §5.2 與 §6.2 envFiles 區塊、§7.1 .env missing-key merge 行為。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
[string] cast 把 $null 轉成 "",if ($null -ne $EnvContent) 始終 true,
Test 3 / 4 的「不傳 -EnvContent」場景仍會建 .env file 導致 envExists=
true,assert 失敗。改用 IsNullOrEmpty 判斷 caller 意圖。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test-PortAvailability 對 docker (8004/5173) + host-native (49100/49101/47998)
五個 port 做 listen owner audit。Get-PidsFromRunDir 讀 scripts\.run\*.pid
判斷 PID 是不是我們上次啟動的 (ourPidFile=true) — 區分 Phase 2 安全
自動清 stale PID vs Phase 3 互動問 'kill 陌生 PID'。

對應 spec §5.2、§6.2 ports 區塊、§7.1/7.2 區別。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PowerShell automatic variable $pid 是 read-only(current process PID),
任何 param($pid) 都會 throw "Cannot overwrite variable pid because it
is read-only or constant"。Test-PortAvailability 的 ProcessNameLookup
default 與 test fake callback 都踩到。改名 $procId。

同步修 plan(Task 6 preflight-ports 與 Task 8 host-native-launcher 的
GetProcessFn callback 範例)避免後續 task subagent 重踩。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test-VolumeAlignment 實作 spec §7.4 方案 A:Ground truth =
.env.web-plane.host-kit 的 RUNTIME_STORAGE_ROOT,host-native conversion-service
在 Phase 4a 啟動前反向對齊。leaf 必須是 'storage'(否則 host-native
conversion-service 寫死的 Resolve-ConversionWorkDir 看不到 storage/ 子目錄)。
status: ALIGNED | MISSING_KEY | WRONG_LEAF。相對路徑以 RepoRoot 為基底
resolve 成絕對路徑。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Assert-Equal 的 -Expected 是 Mandatory,傳 $null 會 "Cannot bind null
to mandatory parameter"。把 'no path' assertion 改用 Assert-True
($null -eq …) 寫法。同步修 plan Task 7 範例。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test-AlreadyRunning / Remove-StalePidFile / Start-HostNativeService /
Wait-HostNativeHealth 抽自 start-all.ps1 既有邏輯,讓 deploy.ps1 共用。
Start-HostNativeConversion / Start-HostNativeKit 是包好的 high-level
入口:前者反向對齊 RUNTIME_STORAGE_ROOT(spec §7.4 方案 A),後者
帶 -ResetUser(memory webrtc-no-video-reset-user-recovery)。

start-all.ps1 本身不動 — 抽 lib 的目的是 deploy.ps1 復用,不 refactor
既有入口(spec §4 / §5.3 Invariant 3)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Test-KitReadyFromLog scan scripts\.run\bim-streaming-server.log
for 'Application started' / 'launching Linux Kit' / 'Streaming started'.
Wait-KitReady poll-loop combines log keyword + signaling port LISTEN
with 90s default timeout (Kit 啟動本來就慢)。Best-effort:timeout
時返回 ready=false,deploy.ps1 視 -StrictPostVerify 決定 warn / fail。

對應 spec §5.2 kit-log-probe、§6.3 Phase 5 Step 6、Decision Summary #5。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
整合 scripts\lib\* 八個 module,實作 spec §6 完整 Phase 0-5 流程:
- Phase 1 read-only preflight + audit JSON
- Phase 2 auto-fix(.venv / .env missing-key / volume alignment append /
  stale PID / 建目錄 / 容器衝突 rm / 第一次 docker build / -Pull)
- Phase 3 互動 guard(陌生 PID 佔 port / .venv WRONG_VERSION)
- Phase 4 嚴格順序啟動(4a conversion → 4b Kit → 4c docker compose)
- Phase 5 post-start verify(best-effort,默認 warn 不 fail)
- Final Summary 成功/失敗各印對應指引

退出碼 0/1/2/3/4/5 對齊 spec §6.3。
不改 start-all.ps1 / start-web-plane-docker.ps1 / start-runtime-manager-docker.ps1
(spec §4 / §13 acceptance criteria #10)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
驗證 spec §9.2 -DryRun 不動真實狀態、印 Phase 1 audit、退 0、不進
Phase 4/5。同時驗 deploy-audit.json 落地(spec §8.3)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… capture

兩個 PowerShell 陷阱:

1. $Args 是 automatic variable(unmatched args array),param($Args) 不會
   bind 傳入值,docker @Args 退化成 docker 沒參數 → 印 help 文,version
   regex match 失敗,deploy.ps1 誤判 docker missing。改名 $ArgList。
   同時 Out-String 把 array of lines 攤平給 -match 用(否則 $Matches
   不 populate)。

2. test-deploy-dryrun 用 2>&1 capture 只抓 error stream,Write-Host 走
   Information stream,內容跑 fly-through 到 host 但 $output 是空。改
   *>&1 抓所有 stream。

全 9 個 test file regression 確認綠。同步修 plan 範例。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7 步驟手動 smoke runbook,對齊 spec §9.3。第一次合進 main 前操作員
跑一次蓋章在 Smoke Pass Log。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
兩個跑實機才暴露的 bug:

1. \$ErrorActionPreference = 'Stop' 在 PowerShell 5.1 對 native command
   (docker compose rm/build/up)的 stderr 進度訊息('Container ... Stopping')
   會 promote 成 terminating error,Phase 2 中途 crash。改 'Continue',
   保留 native cmd stderr 不污染流程,exit code 用 \$LASTEXITCODE 主動檢。

2. .env.web-plane.host-kit Copy-Item from .example 後,\$resolvedEnvFile
   仍指 .example,後續 RUNTIME_STORAGE_ROOT append / docker rm/build/up
   都會用 .example 而非真檔。Copy 後立刻 re-resolve + re-audit volume。

Layer 2 dry-run regression check 仍綠。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Omniverse Kit 啟動完成時 log 寫的是小寫 'app ready'(實機驗證):

    [15.937s] [ext: ezplus.bim_review_stream_streaming-0.1.0] startup
    [19.982s] app ready

之前 kit-log-probe 的 keyword list 沒含這個,Phase 4b 判定 Kit not ready
即使 :49100 已 LISTEN。加 'app ready' 為首位 keyword(其他三個保留以容
Linux Kit / 不同 launcher 版本)。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
子 script 內 \$ErrorActionPreference='Stop' + docker compose up 進度寫
stderr → PowerShell 5.1 把 'Container ... Creating' 印成 NativeCommandError
紅字 trace,嚇到使用者(雖然實際 docker 仍 sync wait 沒真 fail)。

改用 Start-Process new process + RedirectStandardOutput/Error 隔離,讓
子 script 的 stderr 進 docker-compose-up.err.log 而非污染父流程。
父用 $proc.ExitCode 判定。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rwarders

兩個 Phase 3 互動 guard bug 一起修:

1. Phase 2 docker compose rm 改變 docker container 與 wslrelay/com.docker.backend
   等 port forwarder 狀態,但 Phase 3 用 Phase 1 的 stale $ports 資料來問
   互動。Phase 3 開頭 re-audit ports。

2. Docker Desktop 在 Windows 用 wslrelay.exe / com.docker.backend.exe /
   docker.exe / vpnkit.exe / vpnkit-bridge.exe 做 container → host port
   forward,這些不該被當「陌生 PID」要求 user kill。加 whitelist 跳過。

實際情境:user 已啟過 docker container,Phase 1 看到 :8004 被 wslrelay 佔
就標 [ask],Phase 3 問「kill PID 23492 (wslrelay.exe)? (y/N)」誤導 user。
正確做法是讓 Phase 2 docker rm 自然清掉 forwarder,Phase 3 不該問。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
兩個 bug 一起修讓 idempotent re-run 真的 work(5 秒 + Phase 4 全 skip
+ Phase 5 verify 全 200):

1. preflight-ports.ps1:Get-PidsFromRunDir 沿 ParentProcessId 遞迴展開 wrapper
   的子孫 PID。host-native-launcher 用 powershell.exe wrapper spawn 真
   process 是 grand-child(:49100 owner = kit.exe / :49101 = python.exe),
   wrapper PID file 內只有 powershell.exe wrapper,不展開子孫的話 wrapper
   alive 但 kit.exe child 被誤判為「陌生 process」,Phase 3 互動 prompt
   要 user kill 自己的 Kit。

   Resolve-PortStatus 同步把 portPid 強轉 [int],避免 Get-NetTCPConnection
   回的 UInt32 與 hashtable Int32 key type mismatch 讓 ContainsKey 永遠
   回 false。

2. deploy.ps1:Phase 2 docker rm/build + Phase 4c docker up 加 webPlaneRunning
   conditional。`docker compose ps --status running -q coordinator viewer`
   回兩個 container id → web-plane 已 running → 三段都 [skip]。否則仍走
   原本 rm + build + up 路徑(冷啟動)。

   也修 Print-FinalSummary 的 Next/recover 指令文字(stop-all.ps1 沒有
   -SkipCoordinator/-SkipViewer 參數,改成 docker compose down + stop-all)。

實機驗證:
- 冷啟動 deploy.ps1:1m 42s 全綠
- idempotent re-run:5 秒,Phase 4 全 skip,Phase 5 verify 全 200

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 1 cold start + Step 2 first deploy(1m 42s 全綠)+ Step 5 idempotent
re-run(5s + Phase 4 全 skip + verify 全 200)已實機驗。Step 3/4/6/7
需要人類目視 / 手動操作 Docker Desktop,留 PR review 階段補驗 + 後續
回來補蓋章。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 26, 2026 09:58
@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@monkey1sai, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 20 minutes and 28 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4b7fc9e4-b257-47ad-bb7a-683c147b1919

📥 Commits

Reviewing files that changed from the base of the PR and between f6cc7f8 and de24727.

📒 Files selected for processing (5)
  • openspec/changes/add-one-click-deploy-hybrid/design.md
  • openspec/changes/add-one-click-deploy-hybrid/proposal.md
  • openspec/changes/add-one-click-deploy-hybrid/specs/one-click-deploy-hybrid/spec.md
  • openspec/changes/add-one-click-deploy-hybrid/tasks.md
  • scripts/lib/host-native-launcher.ps1
📝 Walkthrough

Walkthrough

This PR adds a complete hybrid one-click deployment system for Windows with NVIDIA GPU support, implementing a five-phase orchestration flow (preflight audit → auto-fix → interactive guard → service startup → health verification) through a main scripts/deploy.ps1 entry point supported by modular preflight checks, service launchers, and health probers, with comprehensive test coverage for all components.

Changes

Hybrid Deployment Orchestration System

Layer / File(s) Summary
Design specification and smoke test runbook
docs/superpowers/specs/2026-05-26-one-click-deploy-design.md, docs/runbooks/one-click-deploy-smoke.md
Design doc specifies five-phase flow (Preflight→Auto-fix→Interactive guard→Start services→Post-verify), Mode C architecture (Docker web plane + host-native Kit/conversion), parameter surface, safety boundaries, and acceptance criteria. Smoke runbook documents seven-step manual validation procedure covering cold start, deploy execution, UI/WebRTC verification, idempotent re-run, forced rebuild, and Docker failure injection.
Preflight audit libraries
scripts/lib/preflight-docker.ps1, scripts/lib/preflight-env.ps1, scripts/lib/preflight-host-native.ps1, scripts/lib/preflight-ports.ps1, scripts/lib/preflight-volume-alignment.ps1, scripts/tests/test-preflight-*.ps1
Six modular checkers: Docker audits CLI/Compose version and engine status; Env parses .env files and reports missing keys; Host-Native validates Python version/kit launcher/NVIDIA driver; Ports maps TCP availability and owning process PIDs; Volume-Alignment validates RUNTIME_STORAGE_ROOT leaf path. Each returns structured objects for Phase 1 decision-making. Comprehensive test coverage validates both happy paths and error conditions.
Service launcher and health monitoring
scripts/lib/host-native-launcher.ps1, scripts/lib/kit-log-probe.ps1, scripts/tests/test-host-native-launcher.ps1, scripts/tests/test-kit-log-probe.ps1
host-native-launcher.ps1 provides PID tracking, process startup with log/err redirection, and health polling; supports launching conversion and Kit services with dependency injection for testability. kit-log-probe.ps1 determines readiness via log keyword scanning and TCP port listening detection. Tests validate PID lifecycle (missing → stale → active), health polling timeout/success, and log/port readiness combinations.
Deployment output and reporting
scripts/lib/deploy-report.ps1, scripts/tests/test-deploy-report.ps1
Standardizes console output via Write-DeployTag (colored status lines + timestamped logs) and Write-DeployHeader (separator headers). Supports ok, fix, ask, skip, warn, fail tags with configurable colors and fail-flag markers. Tests verify tag validation, log file creation, and correct return object fields.
Main orchestrator and test infrastructure
scripts/deploy.ps1, scripts/tests/test-helpers.ps1, scripts/tests/test-deploy-dryrun.ps1
deploy.ps1 implements five-phase flow: (1) audit via preflight checks + serialize results; (2) auto-fix .venv/.env/volume/Docker state unless -DryRun; (3) interactive guard re-audits ports and handles Python version issues; (4) starts host-native services (conversion, Kit) then Docker web plane in child process; (5) probes health endpoints with optional strict enforcement. Shared test helpers provide assertions, sandboxes, and console reporting. DryRun test validates Phase 1 audit output and prevents Phase 4 startup.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • monkey1sai/AI-BIM-governance#88: Adds start-web-plane-docker.ps1 that is invoked by the new scripts/deploy.ps1 in Phase 4 to launch Docker web plane services.

Poem

🐰 A one-click deployment dance,
Five phases orchestrate the prance—
Audit, fix, and start with care,
Services spinning in the air.
✨ Windows + NVIDIA, quite the pair!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing a Mode C one-click deployment orchestrator with deploy.ps1 and supporting library modules, clearly matching the substantial additions across deployment scripts and libraries.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/one-click-deploy-design-2026-05-26

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new one-click Mode C (hybrid) deployment entrypoint (scripts/deploy.ps1) that orchestrates preflight checks, safe auto-fixes, interactive safety prompts, ordered service startup (conversion → Kit → Docker web-plane), and post-start verification. It adds a small PowerShell “library” layer (scripts/lib/*.ps1) plus a suite of PowerShell tests (scripts/tests/test-*.ps1) and supporting design/plan/runbook documentation.

Changes:

  • Add scripts/deploy.ps1 orchestrator implementing phased hybrid deployment with structured logging and exit codes.
  • Add 8 reusable PowerShell modules under scripts/lib/* for reporting, preflight audits, host-native launching, and Kit readiness probing.
  • Add PowerShell unit/integration tests under scripts/tests/* plus design/plan/runbook docs for the deployment workflow.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
docs/runbooks/one-click-deploy-smoke.md Adds manual Layer 3 smoke checklist and a recorded smoke run log.
docs/superpowers/plans/2026-05-26-one-click-deploy.md Implementation plan documenting phases, modules, and testing strategy.
docs/superpowers/specs/2026-05-26-one-click-deploy-design.md Design spec for Mode C one-click deploy architecture, safety boundaries, and acceptance criteria.
scripts/deploy.ps1 New hybrid deployment orchestrator with Phase 1–5 flow, logs, and exit codes.
scripts/lib/deploy-report.ps1 Shared tagged output/logging helpers for deploy reporting.
scripts/lib/host-native-launcher.ps1 Shared process lifecycle helpers to start/health-check host-native conversion and Kit.
scripts/lib/kit-log-probe.ps1 Kit readiness detection via log keyword scan + port listen probing.
scripts/lib/preflight-docker.ps1 Read-only Docker/Compose/env-file audit with injectable probes for tests.
scripts/lib/preflight-env.ps1 Read-only missing-key audit for env files vs corresponding .example files.
scripts/lib/preflight-host-native.ps1 Read-only host-native toolchain audit (.venv, Kit launcher path, nvidia-smi).
scripts/lib/preflight-ports.ps1 Read-only port occupancy audit with PID-file attribution and descendant PID expansion.
scripts/lib/preflight-volume-alignment.ps1 Read-only audit of RUNTIME_STORAGE_ROOT path alignment and leaf validation.
scripts/tests/test-deploy-dryrun.ps1 Integration-style test for deploy.ps1 -DryRun output and audit artifact generation.
scripts/tests/test-deploy-report.ps1 Unit tests for deploy-report tag output + logging behavior.
scripts/tests/test-helpers.ps1 Shared test assert helpers and temp sandbox helpers (non-Pester style).
scripts/tests/test-host-native-launcher.ps1 Unit tests for launcher PID handling and health-check polling logic.
scripts/tests/test-kit-log-probe.ps1 Unit tests for log keyword detection and Wait-KitReady polling behavior.
scripts/tests/test-preflight-docker.ps1 Unit tests for Docker preflight audit logic with injected fakes.
scripts/tests/test-preflight-env.ps1 Unit tests for env missing-key audit across the three target env files.
scripts/tests/test-preflight-host-native.ps1 Unit tests for host-native audit outcomes (missing/wrong version/missing path).
scripts/tests/test-preflight-ports.ps1 Unit tests for port availability + PID-file attribution logic.
scripts/tests/test-preflight-volume-alignment.ps1 Unit tests for volume alignment statuses (ALIGNED/MISSING_KEY/WRONG_LEAF).
Comments suppressed due to low confidence (2)

scripts/deploy.ps1:90

  • Print-FinalSummary assumes each *.pid file has non-empty content and calls .Trim() unconditionally. If a PID file is empty/corrupt (or Get-Content returns $null), this will throw while printing the summary and can mask the real failure cause. Guard against null/empty content before trimming/printing (and ideally handle parse failures gracefully).
        foreach ($pidFile in Get-ChildItem -LiteralPath $RunDir -Filter '*.pid' -ErrorAction SilentlyContinue) {
            $procId = (Get-Content $pidFile.FullName | Select-Object -First 1).Trim()
            Write-Host "  > $($pidFile.BaseName) PID $procId"
        }

docs/superpowers/specs/2026-05-26-one-click-deploy-design.md:449

  • The spec says -DryRun must not write files ("不寫檔"), but the current implementation writes scripts\.run\deploy-audit.json and deploy.log during Phase 1 even under -DryRun (and the integration test asserts this). Please align the spec with the actual behavior (e.g., allow writing logs/audit artifacts under scripts.run during -DryRun), or adjust the implementation/tests to truly be no-write.
`-DryRun` 不能動真實狀態(不啟 process、不動 docker、不寫檔)。


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/deploy.ps1
Comment on lines +31 to +36
Set-StrictMode -Version Latest
# Continue(非 Stop):docker / docker compose 進度寫 stderr,PowerShell 5.1 native
# command 對 stderr 在 Stop policy 下會被 promote 成 terminating error。我們改用
# $LASTEXITCODE 主動檢查,native cmd stderr 只當訊息看。
$ErrorActionPreference = 'Continue'
$script:DeployStart = Get-Date
Comment thread scripts/deploy.ps1
Comment on lines +413 to +424
if ($hostNative.venv -eq 'WRONG_VERSION') {
$prompt = '.venv has wrong Python version (<3.11). Recreate? (will delete .venv) (y/N)'
if ($Force) {
Write-DeployTag -Tag 'fix' -Message "$prompt -> y (--Force)" -LogPath $LogPath | Out-Null
Remove-Item -LiteralPath (Join-Path $RepoRoot '.venv') -Recurse -Force
& python -m venv (Join-Path $RepoRoot '.venv')
} else {
Write-DeployTag -Tag 'ask' -Message $prompt -LogPath $LogPath | Out-Null
$response = Read-Host 'y/N'
if ($response -match '^[Yy]') {
Remove-Item -LiteralPath (Join-Path $RepoRoot '.venv') -Recurse -Force
& python -m venv (Join-Path $RepoRoot '.venv')
Comment thread scripts/deploy.ps1
Comment on lines +364 to +365
Push-Location $RepoRoot
try { docker compose -f compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile pull } finally { Pop-Location }
Comment thread scripts/deploy.ps1
Comment on lines +167 to +172
docker = $docker
hostNative = $hostNative
envFiles = $envFiles
ports = $ports
volume = $volume
envFileUsed = $resolvedEnvFile
Comment on lines +9 to +39
$result = Test-DockerEnvironment `
-DockerCommand { param($Args) "Docker version 27.0.3, build x" } `
-ComposeCommand { param($Args) "Docker Compose version v2.29.0" } `
-EngineProbe { param($Args) @{ ExitCode = 0; Stdout = '{"ServerVersion":"27.0.3"}' } } `
-RepoRoot (New-TestSandbox -Prefix 'preflight-docker')

Assert-True ($result.cliVersion -ne $null) 'cliVersion populated'
Assert-True ($result.composeV2 -eq $true) 'composeV2 true'
Assert-True ($result.engineRunning -eq $true) 'engineRunning true'
Write-TestPass 'happy path returns full audit'

# Test 2: docker CLI 不在 → cliVersion=null + 整體 ok=false
$result = Test-DockerEnvironment `
-DockerCommand { throw 'docker not found' } `
-ComposeCommand { param($Args) '' } `
-EngineProbe { param($Args) @{ ExitCode = 1; Stdout = '' } } `
-RepoRoot (New-TestSandbox -Prefix 'preflight-docker')

Assert-True ($null -eq $result.cliVersion) 'cliVersion null when docker absent'
Assert-True ($result.ok -eq $false) 'overall ok=false'
Write-TestPass 'docker missing flagged'

# Test 3: engine 沒跑 → engineRunning=false
$result = Test-DockerEnvironment `
-DockerCommand { param($Args) "Docker version 27.0.3" } `
-ComposeCommand { param($Args) "Docker Compose version v2.29.0" } `
-EngineProbe { param($Args) @{ ExitCode = 1; Stdout = '' } } `
-RepoRoot (New-TestSandbox -Prefix 'preflight-docker')

Assert-True ($result.engineRunning -eq $false) 'engineRunning=false when engine probe non-zero'
Write-TestPass 'engine not running flagged'
Comment on lines +9 to +17
$result = Test-PortAvailability -RepoRoot (New-TestSandbox -Prefix 'preflight-ports') `
-PortLookup { param($port) $null } `
-ProcessNameLookup { param($procId) $null }
Assert-True ($result.docker.Count -eq 2) 'docker has 2 ports'
Assert-True ($result.hostNative.Count -eq 3) 'hostNative has 3 ports'
foreach ($p in @($result.docker; $result.hostNative)) {
Assert-Equal 'FREE' $p.status "port $($p.port) FREE"
}
Write-TestPass 'all ports free'
Comment on lines +38 to +41
Assert-Throws {
Write-DeployTag -Tag 'unknown' -Message 'x' -LogPath (Join-Path (New-TestSandbox) 'd.log')
} 'unknown tag throws'
Write-TestPass 'unknown tag throws'
Comment on lines +183 to +188
"volumeAlignment": {
"runtimeStorageRoot": null,
"leaf": null,
"status": "MISSING_KEY"
}
}
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Agent Summary

Field Value
Status blocked
Risk high
PR 124
Head docs/one-click-deploy-design-2026-05-26 / f6cc7f8d70168385a6464c8e58f0930bea1b3ac2
Base main / 18aad0f513b703d74ace32a1cb02f35942a0cbad

Blockers

  • [high] Behavior, workflow, code, or repo-boundary changes require an OpenSpec change id or documented exception.

Warnings

  • [medium] GitNexus detect changes did not pass: warning.

Validation Commands

  • C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/tests/test-pr-review-agent.ps1

Checks

  • passed script-level PR review agent tests (scripts)

Human Review Notes

  • Optional AI adapter is not required by policy and was skipped.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f6cc7f8d70

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/deploy.ps1
Comment on lines +178 to +183
if (-not $docker.cliVersion) { $hardFails += 'docker_cli_missing' }
if (-not $docker.composeV2) { $hardFails += 'docker_compose_v2_missing' }
if (-not $docker.engineRunning) { $hardFails += 'docker_engine_not_running' }
if (-not $docker.envFile) { $hardFails += 'env_file_missing_entirely' }
if ($hostNative.nvidiaDriver -eq 'MISSING') { $hardFails += 'nvidia_smi_missing' }
if ($hostNative.kitLauncher -eq 'MISSING_PATH'){ $hardFails += 'kit_launcher_missing' }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Respect skip flags when enforcing Phase 1 hard-fail checks

deploy.ps1 exposes partial-start flags (-SkipDocker, -SkipKit, -SkipConversion), but Phase 1 still unconditionally appends Docker and host-native failures to $hardFails. In practice, -SkipDocker still fails on missing Docker/compose/engine/env, and -SkipKit -SkipConversion still fails on missing NVIDIA/launcher, so the script exits in Phase 1 before the requested reduced mode can run. Gate these hard-fail conditions by the corresponding skip flags so partial deploy/recovery flows actually work.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
if ($Force) {
Write-DeployTag -Tag 'fix' -Message "$prompt -> y (--Force)" -LogPath $LogPath | Out-Null
Remove-Item -LiteralPath (Join-Path $RepoRoot '.venv') -Recurse -Force
& python -m venv (Join-Path $RepoRoot '.venv')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check venv recreation result before continuing startup

In the WRONG_VERSION branch, .venv is deleted and recreated, but the python -m venv call is not validated (unlike the Phase 2 venv creation path). If recreation fails (missing interpreter, permission/path issues), deployment continues into Phase 4 and produces misleading downstream failures instead of a clear Phase 3 stop. Add exit-code/error checks (and ideally the same dependency bootstrap as Phase 2) immediately after this command.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
Comment on lines +477 to +479
} elseif ($webPlaneRunning) {
Write-DeployTag -Tag 'skip' -Message 'Phase 4c docker compose: coordinator + viewer already running' -LogPath $LogPath | Out-Null
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recreate web-plane containers when -Build is requested

When -Build is used and coordinator/viewer are already running, Phase 2 can build new images, but Phase 4c still short-circuits on the stale $webPlaneRunning flag and skips up, so containers are never recreated onto the new image. This makes -Build ineffective for hot reruns where users expect rebuilt code to be applied.

Useful? React with 👍 / 👎.

Comment on lines +128 to +130
$env:STREAMING_CONVERSION_WORK_DIR = $parentRoot
$env:STREAMING_CONVERSION_HOST = '127.0.0.1'
$env:STREAMING_CONVERSION_PORT = "$Port"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Launch conversion service with the bootstrapped venv Python

Phase 2 installs conversion dependencies into .venv, but the conversion launcher only sets host/port/workdir and never pins STREAMING_CONVERSION_PYTHON to that venv interpreter. The downstream runner defaults to global python, so on machines where only the venv has required packages (e.g., uvicorn), Phase 4a reliably times out despite successful bootstrap.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
Write-Host ' > open http://127.0.0.1:8004/ui (coordinator UI / WebRTC entry)'
Write-Host ' > tail scripts\.run\bim-streaming-server.log -Wait'
Write-Host ' > stop all:'
Write-Host ' docker compose -f compose.runtime-manager.yml -f compose.host-kit.yml --env-file .env.web-plane.host-kit down'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Print teardown commands with the resolved env file

The final summary always prints docker compose ... --env-file .env.web-plane.host-kit down even when deploy actually ran with a different env file (for example .env.web-plane.host-kit.example or a custom -EnvFile). In those cases the provided recovery command can fail or target the wrong project, making operational cleanup misleading.

Useful? React with 👍 / 👎.

-Name 'bim-streaming-conversion-service' `
-WorkingDirectory (Join-Path $RepoRoot 'bim-streaming-server') `
-FilePath 'powershell.exe' `
-ArgumentList @('-NoExit','-ExecutionPolicy','Bypass','-NoProfile','-File',$launcher) `

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove -NoExit from background conversion launcher

Start-HostNativeConversion launches PowerShell with -NoExit, so the wrapper process can stay alive even after the conversion script exits/crashes. Because Test-AlreadyRunning only checks whether the PID in *.pid is alive, later deploy runs may incorrectly skip Phase 4a as "already running" while no conversion API is actually serving.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
Comment on lines +398 to +399
Stop-Process -Id $sp.pid -Force -ErrorAction SilentlyContinue
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify stranger PID termination before proceeding

Phase 3 uses Stop-Process -ErrorAction SilentlyContinue for occupied ports, but does not check whether the process actually stopped before continuing. If termination fails (for example due to insufficient privileges), deploy proceeds with ports still occupied and later fails in less actionable places; Phase 3 should re-check liveness/port state and fail immediately with a clear message.

Useful? React with 👍 / 👎.

$exitCode = $LASTEXITCODE

# Test 1: 退 0
Assert-Equal 0 $exitCode '-DryRun exit 0'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decouple dry-run integration test from host prerequisites

test-deploy-dryrun.ps1 executes the real deploy script and asserts exit code 0, but deploy.ps1 -DryRun still performs Phase 1 hard-fail checks (Docker/engine/NVIDIA/env). On environments without those prerequisites, this test fails even though the script behavior is expected there, making the test non-deterministic across developer/CI hosts.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (12)
scripts/tests/test-preflight-env.ps1 (2)

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern as previous files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-preflight-env.ps1` around lines 1 - 7, The file
scripts/tests/test-preflight-env.ps1 should be saved with a UTF-8 BOM so
non-ASCII characters are handled consistently; update the file encoding (that
contains the dot-sourced lines for 'test-helpers.ps1' and the modulePath
assignment and dot-sourcing of preflight-env.ps1) to UTF-8 with BOM using your
editor or a save-encoding step in your CI/tooling so the script starts with the
BOM marker.

9-14: 💤 Low value

Comment is slightly misleading.

The comment states "用 IsNullOrEmpty 區分「沒給」vs「給空字串」" (use IsNullOrEmpty to distinguish "not provided" vs "empty string"), but the code actually treats both cases identically: neither creates a file.

Consider updating the comment to reflect the actual behavior:

# 注意:[string] cast 會把 $null 轉成 "",所以用 IsNullOrEmpty 統一處理 $null 與空字串
# (Note: [string] cast converts $null to "", so use IsNullOrEmpty to handle both $null and empty string identically)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-preflight-env.ps1` around lines 9 - 14, The comment for
New-EnvSandbox is misleading about IsNullOrEmpty distinguishing "not provided"
vs "empty string"; update the comment near the function and the param list
(New-EnvSandbox, param $EnvContent) to state that casting to [string] converts
$null to "" and IsNullOrEmpty therefore treats $null and empty string
identically, e.g., replace the existing line with a note like "注意:[string] cast
會把 $null 轉成 "",所以用 IsNullOrEmpty 統一處理 $null 與空字串" (and include the English
equivalent if desired).
scripts/lib/preflight-volume-alignment.ps1 (2)

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern as previous files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lib/preflight-volume-alignment.ps1` around lines 1 - 7, This file
contains non-ASCII (Chinese) comments and should be saved with a UTF-8 BOM so
editors and Windows PowerShell correctly recognize the encoding; reopen
scripts/lib/preflight-volume-alignment.ps1 and save it encoded as "UTF-8 with
BOM" (or add an explicit BOM) so the header/comments (e.g., the lines above
Set-StrictMode -Version Latest) are preserved and displayed correctly.

65-69: 💤 Low value

Empty catch block could be more explicit.

While the comment "留原值" (keep original value) explains the intent, consider making the fallback more explicit for clarity:

try {
    $resolved = [System.IO.Path]::GetFullPath($resolved)
} catch {
    # GetFullPath failed - keep original value and continue
    # Downstream checks will report the path as-is
}

Alternatively, if observability is important, you could track normalization failures in the return object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lib/preflight-volume-alignment.ps1` around lines 65 - 69, The empty
catch should explicitly state the fallback behavior and optionally record
failure: update the catch block after the
[System.IO.Path]::GetFullPath($resolved) call to include a clear comment like
“GetFullPath failed - keep original $resolved and continue” and optionally add
minimal observability (e.g., set a flag or add a note to the return object) so
downstream code can detect normalization failures; reference the $resolved
variable and the GetFullPath call when making the change.
scripts/tests/test-helpers.ps1 (2)

14-16: 💤 Low value

Array truthiness logic may produce unexpected results with $null.

The current implementation checks $Condition -contains $false, but this doesn't catch $null or other falsy values. For example, @($true, $null) would pass the assertion even though $null is falsy in PowerShell.

Consider using -contains $false together with a check for $null, or refactor to check that all elements are explicitly truthy:

if ($Condition -is [array]) {
    $Condition = ($Condition.Count -gt 0) -and -not ($Condition | Where-Object { -not $_ })
}

This ensures all elements evaluate to $true.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-helpers.ps1` around lines 14 - 16, The array-truthiness
branch for $Condition currently only checks for $false and misses $null/other
falsy values; update the block that handles "if ($Condition -is [array])" so
$Condition becomes true only when Count -gt 0 and no element is falsy (e.g.,
replace the -not ($Condition -contains $false) test with a check that there are
no elements matching {-not $_} such as using -not ($Condition | Where-Object {
-not $_ }) or an equivalent All-true predicate), keeping the change scoped to
the $Condition array-handling code path.

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

The file contains Traditional Chinese comments but lacks a BOM (Byte Order Mark). While this may work in your current environment, adding a UTF-8 BOM ensures consistent character encoding interpretation across different editors and systems, especially when files are opened on machines with different default encodings.

To add BOM in PowerShell:

$content = Get-Content -Path test-helpers.ps1 -Raw
[System.IO.File]::WriteAllText((Resolve-Path test-helpers.ps1), $content, (New-Object System.Text.UTF8Encoding $true))

Or configure your editor to save with UTF-8 BOM.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-helpers.ps1` around lines 1 - 7, The file contains
Traditional Chinese comments but lacks a UTF‑8 BOM; update the file encoding to
UTF‑8 with BOM so editors and systems reliably interpret the non-ASCII text.
Open the file containing the top-level statements (e.g., Set-StrictMode and
$ErrorActionPreference) and rewrite or save it using UTF‑8 with BOM (either via
your editor settings or by programmatically reading the file and writing it back
with a UTF8Encoding that emits a BOM) so the file is stored with the BOM
present.
scripts/lib/preflight-env.ps1 (1)

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern. The file contains Traditional Chinese comments and should have UTF-8 BOM for consistent encoding interpretation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lib/preflight-env.ps1` around lines 1 - 7, This file contains
Traditional Chinese comments at the top (before the Set-StrictMode -Version
Latest line) and should be saved with a UTF-8 BOM so non-ASCII readers interpret
encoding correctly; open the script (the header comment block including the
lines starting "# scripts\lib\preflight-env.ps1" through the comment lines) and
re-save it using UTF-8 with BOM (or prepend the UTF-8 BOM bytes) so the file
encoding is explicitly BOM-marked while leaving the content and the
Set-StrictMode -Version Latest line unchanged.
scripts/lib/preflight-host-native.ps1 (1)

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern as previous files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lib/preflight-host-native.ps1` around lines 1 - 7, This file contains
non-ASCII characters in the header and should be saved with a UTF-8 BOM to avoid
encoding issues; open the script (containing Set-StrictMode and the
Test-HostNativeEnvironment function) in your editor/CI step and re-save or
transcode the file as UTF-8 with BOM (or remove/replace the non-ASCII
characters) so the header comment is preserved correctly across tools that
expect a BOM.
scripts/tests/test-preflight-docker.ps1 (2)

10-12: 💤 Low value

Parameter name $Args may cause confusion.

While technically valid in a param() block, naming a parameter $Args can be confusing since PowerShell has an automatic $Args variable. The parameters are declared but unused in these test mocks, so consider either:

  1. Removing the unused parameters (since mocks return static strings), or
  2. Renaming to $ArgList or $Arguments to match the module's convention and avoid confusion
-DockerCommand { "Docker version 27.0.3, build x" } `
-ComposeCommand { "Docker Compose version v2.29.0" } `
-EngineProbe { @{ ExitCode = 0; Stdout = '{"ServerVersion":"27.0.3"}' } } `
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-preflight-docker.ps1` around lines 10 - 12, The mock
script blocks DockerCommand, ComposeCommand, and EngineProbe declare unused
parameters named $Args which can be confused with PowerShell's automatic $Args;
remove the param() declarations (or rename to $ArgList/$Arguments) and return
the static values directly so DockerCommand returns the version string,
ComposeCommand returns the compose string, and EngineProbe returns the hashtable
@{ ExitCode = 0; Stdout = '{"ServerVersion":"27.0.3"}' } as shown in the review.

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern as previous files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-preflight-docker.ps1` around lines 1 - 7, This test script
contains non-ASCII content and should be saved with a UTF-8 BOM so PowerShell
reads characters correctly; reopen scripts/tests/test-preflight-docker.ps1 (and
any referenced helper files like test-helpers.ps1 and
scripts/lib/preflight-docker.ps1 if they contain non-ASCII text) and re-save
them with "UTF-8 with BOM" encoding (or add the BOM header) using your editor or
an encoding-aware save command to ensure correct parsing at runtime.
scripts/lib/preflight-docker.ps1 (1)

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern as test-helpers.ps1. The file contains Traditional Chinese comments and should have UTF-8 BOM for consistent encoding interpretation across systems.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lib/preflight-docker.ps1` around lines 1 - 7, The file contains
Traditional Chinese comments (the header lines and the Set-StrictMode -Version
Latest statement) but lacks a UTF-8 BOM; re-save this script with UTF-8 encoding
with BOM so non-ASCII characters are interpreted consistently across platforms,
e.g., open the file containing the header comments and the Set-StrictMode
-Version Latest line and change the file encoding to "UTF-8 with BOM" (or use an
editor/CLI tool to write a BOM) without altering the script contents.
scripts/lib/preflight-ports.ps1 (1)

1-7: 💤 Low value

Consider adding UTF-8 BOM for non-ASCII characters.

Same BOM encoding concern as previous files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lib/preflight-ports.ps1` around lines 1 - 7, This file contains
non-ASCII Chinese characters in the header comment and should be saved with a
UTF-8 BOM so PowerShell correctly interprets them; open
scripts/lib/preflight-ports.ps1 (look for the header comment and the
Set-StrictMode -Version Latest line), change the file encoding to "UTF-8 with
BOM" (or re-save via your editor/CI tooling with a BOM) and commit the
re-encoded file so Windows/PowerShell won't misread the non-ASCII characters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/specs/2026-05-26-one-click-deploy-design.md`:
- Line 46: Update the spec text that describes the orchestrator size to match
the current implementation: change the phrase "100-150 行薄 orchestrator" to
either "模組化 orchestrator(目前約 490 行)" or remove the hard line-count limit; edit
the same phrasing at the other occurrence referenced (line 108) as well; ensure
you update the description that mentions deploy.ps1 and scripts\lib\*.ps1 so the
docs reflect the actual deploy.ps1 size and modularization rather than the
outdated 100–150 line claim.
- Line 538: Update the Acceptance Criteria text that currently reads "Pester 8 個
module test 全綠" to instead state "純 PowerShell + Assert-* helpers 8 個 module
test 全綠" (or similar wording that uses "純 PowerShell + Assert-* helpers") so it
matches the earlier statement that the repo does not use Pester; ensure you
replace that exact phrase found in the Acceptance Criteria section.
- Line 15: Several fenced code blocks in the document are missing language
markers (MD040); locate the bare code fences (``` ) around the examples (the
blocks noted in the review) and add the appropriate language tag (e.g.,
powershell, json, or text) after the opening backticks so each block becomes
```powershell / ```json / ```text as appropriate to the snippet content,
ensuring lint passes and readability is improved.

In `@scripts/deploy.ps1`:
- Around line 413-425: The WRONG_VERSION branch recreates the virtualenv (checks
hostNative.venv, uses Remove-Item and & python -m venv) but does not reinstall
dependencies, leaving host-native without required packages; after both places
where the script creates the .venv (both the $Force and interactive branches
that call & python -m venv) invoke the same requirements-install logic used
elsewhere in the script (the existing pip install -r ... sequence), reuse or
extract that installation into a shared routine and call it after venv creation,
and if the pip install fails return/exit with the same ExitCode 2 as the
original requirements-install flow so failures are propagated.
- Around line 182-184: The hard-fail checks should respect the CLI flags by only
adding 'nvidia_smi_missing' when conversion is not skipped and only adding
'kit_launcher_missing' when kit steps are not skipped: update the conditions
around $hostNative.nvidiaDriver and $hostNative.kitLauncher so they include
checks like (-not $SkipConversion) for the NVIDIA check and (-not $SkipKit) for
the kit launcher check before appending to $hardFails; leave the $volume.status
/ 'runtime_storage_root_wrong_leaf' check unchanged.
- Around line 398-404: After calling Stop-Process on $sp.pid (both places where
Stop-Process -Id $sp.pid -Force -ErrorAction SilentlyContinue is used), verify
the PID was actually terminated: attempt to Get-Process -Id $sp.pid (or
Wait-Process with a short timeout) and if the process still exists or retrieval
fails due to permissions, write a failure DeployTag via Write-DeployTag (include
the PID and the error/reason), and either retry or abort the deploy flow so
Phase 4 does not continue with an inaccurate state; update the Stop-Process call
sites and the subsequent logging to perform this existence check and branch on
success/failure accordingly.
- Around line 365-366: After running the docker compose pull command, check
$LASTEXITCODE and treat non-zero as failure: capture the exit code immediately
after the try/finally block that runs "docker compose -f
compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile
pull", and if $LASTEXITCODE -ne 0, write an error (e.g., Write-Error or
Write-Host) and Exit with that code (or throw) so the script stops and does not
increment $fixActions; ensure Pop-Location still runs (keep the finally) but
perform the exit/check before the line that increments $fixActions.

In `@scripts/tests/test-deploy-report.ps1`:
- Around line 38-40: The test creates a sandbox inside Assert-Throws via
New-TestSandbox causing leftover temp dirs; refactor so you call New-TestSandbox
before the Assert-Throws, capture the sandbox path in a variable, then call
Write-DeployTag inside Assert-Throws using that path, and ensure you wrap the
test in try/finally (or use try/catch/finally) to always call Remove-TestSandbox
with the captured sandbox path in the finally block to guarantee cleanup;
reference the functions Assert-Throws, New-TestSandbox, Remove-TestSandbox and
Write-DeployTag when making these changes.

In `@scripts/tests/test-preflight-ports.ps1`:
- Around line 9-17: The test creates a sandbox inline via New-TestSandbox when
calling Test-PortAvailability but never removes it; change the test to capture
the sandbox handle (e.g. assign New-TestSandbox to a variable), pass that
sandbox/root into Test-PortAvailability, and ensure you call Remove-TestSandbox
on that handle at the end (preferably in a try/finally so Remove-TestSandbox
runs even if assertions fail); reference the New-TestSandbox call, the
Test-PortAvailability invocation and add a Remove-TestSandbox cleanup step tied
to the created sandbox.

---

Nitpick comments:
In `@scripts/lib/preflight-docker.ps1`:
- Around line 1-7: The file contains Traditional Chinese comments (the header
lines and the Set-StrictMode -Version Latest statement) but lacks a UTF-8 BOM;
re-save this script with UTF-8 encoding with BOM so non-ASCII characters are
interpreted consistently across platforms, e.g., open the file containing the
header comments and the Set-StrictMode -Version Latest line and change the file
encoding to "UTF-8 with BOM" (or use an editor/CLI tool to write a BOM) without
altering the script contents.

In `@scripts/lib/preflight-env.ps1`:
- Around line 1-7: This file contains Traditional Chinese comments at the top
(before the Set-StrictMode -Version Latest line) and should be saved with a
UTF-8 BOM so non-ASCII readers interpret encoding correctly; open the script
(the header comment block including the lines starting "#
scripts\lib\preflight-env.ps1" through the comment lines) and re-save it using
UTF-8 with BOM (or prepend the UTF-8 BOM bytes) so the file encoding is
explicitly BOM-marked while leaving the content and the Set-StrictMode -Version
Latest line unchanged.

In `@scripts/lib/preflight-host-native.ps1`:
- Around line 1-7: This file contains non-ASCII characters in the header and
should be saved with a UTF-8 BOM to avoid encoding issues; open the script
(containing Set-StrictMode and the Test-HostNativeEnvironment function) in your
editor/CI step and re-save or transcode the file as UTF-8 with BOM (or
remove/replace the non-ASCII characters) so the header comment is preserved
correctly across tools that expect a BOM.

In `@scripts/lib/preflight-ports.ps1`:
- Around line 1-7: This file contains non-ASCII Chinese characters in the header
comment and should be saved with a UTF-8 BOM so PowerShell correctly interprets
them; open scripts/lib/preflight-ports.ps1 (look for the header comment and the
Set-StrictMode -Version Latest line), change the file encoding to "UTF-8 with
BOM" (or re-save via your editor/CI tooling with a BOM) and commit the
re-encoded file so Windows/PowerShell won't misread the non-ASCII characters.

In `@scripts/lib/preflight-volume-alignment.ps1`:
- Around line 1-7: This file contains non-ASCII (Chinese) comments and should be
saved with a UTF-8 BOM so editors and Windows PowerShell correctly recognize the
encoding; reopen scripts/lib/preflight-volume-alignment.ps1 and save it encoded
as "UTF-8 with BOM" (or add an explicit BOM) so the header/comments (e.g., the
lines above Set-StrictMode -Version Latest) are preserved and displayed
correctly.
- Around line 65-69: The empty catch should explicitly state the fallback
behavior and optionally record failure: update the catch block after the
[System.IO.Path]::GetFullPath($resolved) call to include a clear comment like
“GetFullPath failed - keep original $resolved and continue” and optionally add
minimal observability (e.g., set a flag or add a note to the return object) so
downstream code can detect normalization failures; reference the $resolved
variable and the GetFullPath call when making the change.

In `@scripts/tests/test-helpers.ps1`:
- Around line 14-16: The array-truthiness branch for $Condition currently only
checks for $false and misses $null/other falsy values; update the block that
handles "if ($Condition -is [array])" so $Condition becomes true only when Count
-gt 0 and no element is falsy (e.g., replace the -not ($Condition -contains
$false) test with a check that there are no elements matching {-not $_} such as
using -not ($Condition | Where-Object { -not $_ }) or an equivalent All-true
predicate), keeping the change scoped to the $Condition array-handling code
path.
- Around line 1-7: The file contains Traditional Chinese comments but lacks a
UTF‑8 BOM; update the file encoding to UTF‑8 with BOM so editors and systems
reliably interpret the non-ASCII text. Open the file containing the top-level
statements (e.g., Set-StrictMode and $ErrorActionPreference) and rewrite or save
it using UTF‑8 with BOM (either via your editor settings or by programmatically
reading the file and writing it back with a UTF8Encoding that emits a BOM) so
the file is stored with the BOM present.

In `@scripts/tests/test-preflight-docker.ps1`:
- Around line 10-12: The mock script blocks DockerCommand, ComposeCommand, and
EngineProbe declare unused parameters named $Args which can be confused with
PowerShell's automatic $Args; remove the param() declarations (or rename to
$ArgList/$Arguments) and return the static values directly so DockerCommand
returns the version string, ComposeCommand returns the compose string, and
EngineProbe returns the hashtable @{ ExitCode = 0; Stdout =
'{"ServerVersion":"27.0.3"}' } as shown in the review.
- Around line 1-7: This test script contains non-ASCII content and should be
saved with a UTF-8 BOM so PowerShell reads characters correctly; reopen
scripts/tests/test-preflight-docker.ps1 (and any referenced helper files like
test-helpers.ps1 and scripts/lib/preflight-docker.ps1 if they contain non-ASCII
text) and re-save them with "UTF-8 with BOM" encoding (or add the BOM header)
using your editor or an encoding-aware save command to ensure correct parsing at
runtime.

In `@scripts/tests/test-preflight-env.ps1`:
- Around line 1-7: The file scripts/tests/test-preflight-env.ps1 should be saved
with a UTF-8 BOM so non-ASCII characters are handled consistently; update the
file encoding (that contains the dot-sourced lines for 'test-helpers.ps1' and
the modulePath assignment and dot-sourcing of preflight-env.ps1) to UTF-8 with
BOM using your editor or a save-encoding step in your CI/tooling so the script
starts with the BOM marker.
- Around line 9-14: The comment for New-EnvSandbox is misleading about
IsNullOrEmpty distinguishing "not provided" vs "empty string"; update the
comment near the function and the param list (New-EnvSandbox, param $EnvContent)
to state that casting to [string] converts $null to "" and IsNullOrEmpty
therefore treats $null and empty string identically, e.g., replace the existing
line with a note like "注意:[string] cast 會把 $null 轉成 "",所以用 IsNullOrEmpty 統一處理
$null 與空字串" (and include the English equivalent if desired).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac691764-0e4d-4ba8-b93d-4c869ec7520d

📥 Commits

Reviewing files that changed from the base of the PR and between 18aad0f and f6cc7f8.

📒 Files selected for processing (22)
  • docs/runbooks/one-click-deploy-smoke.md
  • docs/superpowers/plans/2026-05-26-one-click-deploy.md
  • docs/superpowers/specs/2026-05-26-one-click-deploy-design.md
  • scripts/deploy.ps1
  • scripts/lib/deploy-report.ps1
  • scripts/lib/host-native-launcher.ps1
  • scripts/lib/kit-log-probe.ps1
  • scripts/lib/preflight-docker.ps1
  • scripts/lib/preflight-env.ps1
  • scripts/lib/preflight-host-native.ps1
  • scripts/lib/preflight-ports.ps1
  • scripts/lib/preflight-volume-alignment.ps1
  • scripts/tests/test-deploy-dryrun.ps1
  • scripts/tests/test-deploy-report.ps1
  • scripts/tests/test-helpers.ps1
  • scripts/tests/test-host-native-launcher.ps1
  • scripts/tests/test-kit-log-probe.ps1
  • scripts/tests/test-preflight-docker.ps1
  • scripts/tests/test-preflight-env.ps1
  • scripts/tests/test-preflight-host-native.ps1
  • scripts/tests/test-preflight-ports.ps1
  • scripts/tests/test-preflight-volume-alignment.ps1


成功標準:

```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

請為 fenced code block 補上語言標記

這些 code fence 缺少語言標記(MD040),建議補上 powershell / json / text,避免 lint 噪音並提升可讀性。

Also applies to: 80-80, 106-106, 138-138, 263-263, 288-288, 312-312, 337-337, 388-388

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 15-15: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-26-one-click-deploy-design.md` at line 15,
Several fenced code blocks in the document are missing language markers (MD040);
locate the bare code fences (``` ) around the examples (the blocks noted in the
review) and add the appropriate language tag (e.g., powershell, json, or text)
after the opening backticks so each block becomes ```powershell / ```json /
```text as appropriate to the snippet content, ensuring lint passes and
readability is improved.


brainstorming 過程拍板:

1. **Approach B**:`deploy.ps1` 是薄 orchestrator(100-150 行),所有檢查 / 修復 / 啟動邏輯分到 `scripts\lib\*.ps1` modules。Module 全部 read-only(或在明確 phase 才動手),便於 Pester 單測。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

請將 orchestrator 行數描述改為符合現況實作

這裡寫「100-150 行薄 orchestrator」,但本 PR 的實作已是約 490 行;規格若不更新,會直接誤導後續驗收與重構決策。建議改成「模組化 orchestrator(目前約 490 行)」或移除具體行數上限。

As per coding guidelines, 「When wiki conflicts with implementation, prioritize implementation and update wiki accordingly」.

Also applies to: 108-108

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-26-one-click-deploy-design.md` at line 46,
Update the spec text that describes the orchestrator size to match the current
implementation: change the phrase "100-150 行薄 orchestrator" to either "模組化
orchestrator(目前約 490 行)" or remove the hard line-count limit; edit the same
phrasing at the other occurrence referenced (line 108) as well; ensure you
update the description that mentions deploy.ps1 and scripts\lib\*.ps1 so the
docs reflect the actual deploy.ps1 size and modularization rather than the
outdated 100–150 line claim.

3. ✅ `-DryRun` 不動任何真實狀態,可預覽 fix plan
4. ✅ `-Build` 強制 rebuild docker image
5. ✅ `.env` 已有 key 的實值在所有路徑下都不被覆寫
6. ✅ Layer 1 Pester 8 個 module test 全綠

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Acceptance Criteria 的測試框架名稱需修正一致

此處寫「Pester 8 個 module test 全綠」,但前文(Line 386)已明確說 repo 不用 Pester。請統一為「純 PowerShell + Assert-* helpers」。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/superpowers/specs/2026-05-26-one-click-deploy-design.md` at line 538,
Update the Acceptance Criteria text that currently reads "Pester 8 個 module test
全綠" to instead state "純 PowerShell + Assert-* helpers 8 個 module test 全綠" (or
similar wording that uses "純 PowerShell + Assert-* helpers") so it matches the
earlier statement that the repo does not use Pester; ensure you replace that
exact phrase found in the Acceptance Criteria section.

Comment thread scripts/deploy.ps1
Comment on lines +182 to +184
if ($hostNative.nvidiaDriver -eq 'MISSING') { $hardFails += 'nvidia_smi_missing' }
if ($hostNative.kitLauncher -eq 'MISSING_PATH'){ $hardFails += 'kit_launcher_missing' }
if ($volume.status -eq 'WRONG_LEAF') { $hardFails += 'runtime_storage_root_wrong_leaf' }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Hard fail 條件應尊重 -SkipKit / -SkipConversion

Line 182-184 目前即使使用者明確 -SkipKit 或 -SkipConversion,仍可能因 kit_launcher_missing / nvidia_smi_missing 直接失敗,和參數語意衝突。

建議修正
-if ($hostNative.nvidiaDriver -eq 'MISSING')   { $hardFails += 'nvidia_smi_missing' }
-if ($hostNative.kitLauncher -eq 'MISSING_PATH'){ $hardFails += 'kit_launcher_missing' }
+if ((-not $SkipConversion -or -not $SkipKit) -and $hostNative.nvidiaDriver -eq 'MISSING') {
+    $hardFails += 'nvidia_smi_missing'
+}
+if ((-not $SkipKit) -and $hostNative.kitLauncher -eq 'MISSING_PATH') {
+    $hardFails += 'kit_launcher_missing'
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ($hostNative.nvidiaDriver -eq 'MISSING') { $hardFails += 'nvidia_smi_missing' }
if ($hostNative.kitLauncher -eq 'MISSING_PATH'){ $hardFails += 'kit_launcher_missing' }
if ($volume.status -eq 'WRONG_LEAF') { $hardFails += 'runtime_storage_root_wrong_leaf' }
if ((-not $SkipConversion -or -not $SkipKit) -and $hostNative.nvidiaDriver -eq 'MISSING') {
$hardFails += 'nvidia_smi_missing'
}
if ((-not $SkipKit) -and $hostNative.kitLauncher -eq 'MISSING_PATH') {
$hardFails += 'kit_launcher_missing'
}
if ($volume.status -eq 'WRONG_LEAF') { $hardFails += 'runtime_storage_root_wrong_leaf' }
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'deploy.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy.ps1` around lines 182 - 184, The hard-fail checks should
respect the CLI flags by only adding 'nvidia_smi_missing' when conversion is not
skipped and only adding 'kit_launcher_missing' when kit steps are not skipped:
update the conditions around $hostNative.nvidiaDriver and
$hostNative.kitLauncher so they include checks like (-not $SkipConversion) for
the NVIDIA check and (-not $SkipKit) for the kit launcher check before appending
to $hardFails; leave the $volume.status / 'runtime_storage_root_wrong_leaf'
check unchanged.

Comment thread scripts/deploy.ps1
Comment on lines +365 to +366
try { docker compose -f compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile pull } finally { Pop-Location }
$fixActions++

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

docker compose pull 缺少失敗檢查

Line 365-366 執行 pull 後不檢查 $LASTEXITCODE,pull 失敗會被當成成功流程繼續,容易造成後續啟動失敗且訊號延遲。

建議修正
 Push-Location $RepoRoot
 try { docker compose -f compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile pull } finally { Pop-Location }
+$pullExit = $LASTEXITCODE
+if ($pullExit -ne 0) {
+    Write-DeployTag -Tag 'fail' -Message "docker compose pull failed (exit=$pullExit)" -LogPath $LogPath | Out-Null
+    Print-FinalSummary -ExitCode 2 -FailedPhase 'Phase 2 (docker pull)'
+    exit 2
+}
 $fixActions++
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try { docker compose -f compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile pull } finally { Pop-Location }
$fixActions++
try { docker compose -f compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile pull } finally { Pop-Location }
$pullExit = $LASTEXITCODE
if ($pullExit -ne 0) {
Write-DeployTag -Tag 'fail' -Message "docker compose pull failed (exit=$pullExit)" -LogPath $LogPath | Out-Null
Print-FinalSummary -ExitCode 2 -FailedPhase 'Phase 2 (docker pull)'
exit 2
}
$fixActions++
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'deploy.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy.ps1` around lines 365 - 366, After running the docker compose
pull command, check $LASTEXITCODE and treat non-zero as failure: capture the
exit code immediately after the try/finally block that runs "docker compose -f
compose.runtime-manager.yml -f compose.host-kit.yml --env-file $resolvedEnvFile
pull", and if $LASTEXITCODE -ne 0, write an error (e.g., Write-Error or
Write-Host) and Exit with that code (or throw) so the script stops and does not
increment $fixActions; ensure Pop-Location still runs (keep the finally) but
perform the exit/check before the line that increments $fixActions.

Comment thread scripts/deploy.ps1
Comment on lines +398 to +404
Stop-Process -Id $sp.pid -Force -ErrorAction SilentlyContinue
} else {
Write-DeployTag -Tag 'ask' -Message $prompt -LogPath $LogPath | Out-Null
$response = Read-Host 'y/N'
if ($response -match '^[Yy]') {
Stop-Process -Id $sp.pid -Force -ErrorAction SilentlyContinue
Write-DeployTag -Tag 'fix' -Message "killed PID $($sp.pid)" -LogPath $LogPath | Out-Null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

強制停止 stranger PID 後需驗證是否真的成功

Line 398/403 用 Stop-Process ... -ErrorAction SilentlyContinue 後直接前進;若因權限或 PID 失效未成功停止,Phase 4 會在更晚失敗且訊息不精準。

建議修正
-Stop-Process -Id $sp.pid -Force -ErrorAction SilentlyContinue
+Stop-Process -Id $sp.pid -Force -ErrorAction SilentlyContinue
+Start-Sleep -Milliseconds 300
+if (Get-Process -Id $sp.pid -ErrorAction SilentlyContinue) {
+    Write-DeployTag -Tag 'fail' -Message "failed to stop PID $($sp.pid)" -LogPath $LogPath | Out-Null
+    Print-FinalSummary -ExitCode 3 -FailedPhase 'Phase 3 (kill process failed)'
+    exit 3
+}
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'deploy.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy.ps1` around lines 398 - 404, After calling Stop-Process on
$sp.pid (both places where Stop-Process -Id $sp.pid -Force -ErrorAction
SilentlyContinue is used), verify the PID was actually terminated: attempt to
Get-Process -Id $sp.pid (or Wait-Process with a short timeout) and if the
process still exists or retrieval fails due to permissions, write a failure
DeployTag via Write-DeployTag (include the PID and the error/reason), and either
retry or abort the deploy flow so Phase 4 does not continue with an inaccurate
state; update the Stop-Process call sites and the subsequent logging to perform
this existence check and branch on success/failure accordingly.

Comment thread scripts/deploy.ps1
Comment on lines +413 to +425
if ($hostNative.venv -eq 'WRONG_VERSION') {
$prompt = '.venv has wrong Python version (<3.11). Recreate? (will delete .venv) (y/N)'
if ($Force) {
Write-DeployTag -Tag 'fix' -Message "$prompt -> y (--Force)" -LogPath $LogPath | Out-Null
Remove-Item -LiteralPath (Join-Path $RepoRoot '.venv') -Recurse -Force
& python -m venv (Join-Path $RepoRoot '.venv')
} else {
Write-DeployTag -Tag 'ask' -Message $prompt -LogPath $LogPath | Out-Null
$response = Read-Host 'y/N'
if ($response -match '^[Yy]') {
Remove-Item -LiteralPath (Join-Path $RepoRoot '.venv') -Recurse -Force
& python -m venv (Join-Path $RepoRoot '.venv')
} else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

重建 .venv 後未安裝相依套件

Line 417-425 在 WRONG_VERSION 分支只重建 .venv,但沒有重跑 pip install -r ...。這會讓 Phase 4 以缺套件狀態啟動 host-native 服務。

建議重用 Line 214-225 的 requirements 安裝流程(或抽成共用函式)並在失敗時沿用 ExitCode 2。

🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'deploy.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/deploy.ps1` around lines 413 - 425, The WRONG_VERSION branch
recreates the virtualenv (checks hostNative.venv, uses Remove-Item and & python
-m venv) but does not reinstall dependencies, leaving host-native without
required packages; after both places where the script creates the .venv (both
the $Force and interactive branches that call & python -m venv) invoke the same
requirements-install logic used elsewhere in the script (the existing pip
install -r ... sequence), reuse or extract that installation into a shared
routine and call it after venv creation, and if the pip install fails
return/exit with the same ExitCode 2 as the original requirements-install flow
so failures are propagated.

Comment on lines +38 to +40
Assert-Throws {
Write-DeployTag -Tag 'unknown' -Message 'x' -LogPath (Join-Path (New-TestSandbox) 'd.log')
} 'unknown tag throws'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

避免在 Assert-Throws 內建立未清理的 sandbox

Line 39 目前會建立一次 New-TestSandbox 但沒有 Remove-TestSandbox,長期跑測試容易累積暫存目錄。建議把 sandbox 提前建立並在 finally 清理。

建議修正
-Assert-Throws {
-    Write-DeployTag -Tag 'unknown' -Message 'x' -LogPath (Join-Path (New-TestSandbox) 'd.log')
-} 'unknown tag throws'
+$sandbox = New-TestSandbox -Prefix 'deploy-report'
+try {
+    Assert-Throws {
+        Write-DeployTag -Tag 'unknown' -Message 'x' -LogPath (Join-Path $sandbox 'd.log')
+    } 'unknown tag throws'
+}
+finally { Remove-TestSandbox -Path $sandbox }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Assert-Throws {
Write-DeployTag -Tag 'unknown' -Message 'x' -LogPath (Join-Path (New-TestSandbox) 'd.log')
} 'unknown tag throws'
$sandbox = New-TestSandbox -Prefix 'deploy-report'
try {
Assert-Throws {
Write-DeployTag -Tag 'unknown' -Message 'x' -LogPath (Join-Path $sandbox 'd.log')
} 'unknown tag throws'
}
finally { Remove-TestSandbox -Path $sandbox }
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'test-deploy-report.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-deploy-report.ps1` around lines 38 - 40, The test creates
a sandbox inside Assert-Throws via New-TestSandbox causing leftover temp dirs;
refactor so you call New-TestSandbox before the Assert-Throws, capture the
sandbox path in a variable, then call Write-DeployTag inside Assert-Throws using
that path, and ensure you wrap the test in try/finally (or use
try/catch/finally) to always call Remove-TestSandbox with the captured sandbox
path in the finally block to guarantee cleanup; reference the functions
Assert-Throws, New-TestSandbox, Remove-TestSandbox and Write-DeployTag when
making these changes.

Comment on lines +9 to +17
$result = Test-PortAvailability -RepoRoot (New-TestSandbox -Prefix 'preflight-ports') `
-PortLookup { param($port) $null } `
-ProcessNameLookup { param($procId) $null }
Assert-True ($result.docker.Count -eq 2) 'docker has 2 ports'
Assert-True ($result.hostNative.Count -eq 3) 'hostNative has 3 ports'
foreach ($p in @($result.docker; $result.hostNative)) {
Assert-Equal 'FREE' $p.status "port $($p.port) FREE"
}
Write-TestPass 'all ports free'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

補上 Test 1 sandbox 清理,避免測試殘留目錄。

Line 9 目前直接在參數中建立 sandbox,沒有對應 Remove-TestSandbox,會造成暫存資料累積。

建議修正
-# Test 1: 全 FREE
-$result = Test-PortAvailability -RepoRoot (New-TestSandbox -Prefix 'preflight-ports') `
-    -PortLookup { param($port) $null } `
-    -ProcessNameLookup { param($procId) $null }
-Assert-True ($result.docker.Count -eq 2) 'docker has 2 ports'
-Assert-True ($result.hostNative.Count -eq 3) 'hostNative has 3 ports'
-foreach ($p in @($result.docker; $result.hostNative)) {
-    Assert-Equal 'FREE' $p.status "port $($p.port) FREE"
-}
-Write-TestPass 'all ports free'
+# Test 1: 全 FREE
+$sandbox = New-TestSandbox -Prefix 'preflight-ports'
+try {
+    $result = Test-PortAvailability -RepoRoot $sandbox `
+        -PortLookup { param($port) $null } `
+        -ProcessNameLookup { param($procId) $null }
+    Assert-True ($result.docker.Count -eq 2) 'docker has 2 ports'
+    Assert-True ($result.hostNative.Count -eq 3) 'hostNative has 3 ports'
+    foreach ($p in @($result.docker; $result.hostNative)) {
+        Assert-Equal 'FREE' $p.status "port $($p.port) FREE"
+    }
+    Write-TestPass 'all ports free'
+}
+finally { Remove-TestSandbox -Path $sandbox }
🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'test-preflight-ports.ps1'

(PSUseBOMForUnicodeEncodedFile)


[warning] 10-10: The parameter 'port' has been declared but not used.

(PSReviewUnusedParameter)


[warning] 11-11: The parameter 'procId' has been declared but not used.

(PSReviewUnusedParameter)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test-preflight-ports.ps1` around lines 9 - 17, The test creates
a sandbox inline via New-TestSandbox when calling Test-PortAvailability but
never removes it; change the test to capture the sandbox handle (e.g. assign
New-TestSandbox to a variable), pass that sandbox/root into
Test-PortAvailability, and ensure you call Remove-TestSandbox on that handle at
the end (preferably in a try/finally so Remove-TestSandbox runs even if
assertions fail); reference the New-TestSandbox call, the Test-PortAvailability
invocation and add a Remove-TestSandbox cleanup step tied to the created
sandbox.

實機 dispatch IFC conversion job 失敗:

    error.code = invalid_ifc_input
    error.message = local IFC path is outside storage_root:
                    C:\Repos\active\iot\AI-BIM-governance\storage\ifc-cache\...\source.ifc

ifc2usdc_powershell_adapter.py 用 env var STORAGE_ROOT(line 75-76)做
dispatch payload host_local_path 必須在其下的 sandbox 根,沒設就 fall
back 到 Path.cwd()(= bim-streaming-server 子目錄)。coordinator 給的
host_local_path = <RuntimeStorageRoot>\ifc-cache\<job>\source.ifc,
所以 STORAGE_ROOT 必須對齊 RuntimeStorageRoot,IFC path 才落在 sandbox 內。

之前只設 STREAMING_CONVERSION_WORK_DIR(launcher 用,不是 storage_root)。
加 STORAGE_ROOT = RuntimeStorageRoot 讓對齊真的生效。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Agent Summary

Field Value
Status blocked
Risk high
PR 124
Head docs/one-click-deploy-design-2026-05-26 / 4052c0989fdcb4ff56d7dbe96c0d3406135053fc
Base main / 18aad0f513b703d74ace32a1cb02f35942a0cbad

Blockers

  • [high] Behavior, workflow, code, or repo-boundary changes require an OpenSpec change id or documented exception.

Warnings

  • [medium] GitNexus detect changes did not pass: warning.

Validation Commands

  • C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/tests/test-pr-review-agent.ps1

Checks

  • passed script-level PR review agent tests (scripts)

Human Review Notes

  • Optional AI adapter is not required by policy and was skipped.

PR #124 的 deploy.ps1 + scripts\lib\* 是行為 / repo-boundary 變更,
pr-review-agent gate 要求 OpenSpec change id 或 documented exception。
這次補建 OpenSpec change(proposal / design / tasks / specs/one-click-deploy-hybrid/spec.md),
讓 PR 通過 CI gate。

內容:
- proposal:Mode C hybrid 一鍵部屬的「為何 / 變更內容 / 能力 / 影響」
- design:Phase 0-5 流程 / 退出碼 / Volume 對齊方案 A / 12 個 PowerShell 5.1 trap
- tasks:9 章節對應實際 commit history,9.1-9.7 全綠,9.8 PR review 補
- specs/one-click-deploy-hybrid/spec.md:9 個 capability requirement + 對應 scenario

`openspec validate add-one-click-deploy-hybrid` 本機 pass。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Agent Summary

Field Value
Status warning
Risk medium
PR 124
Head docs/one-click-deploy-design-2026-05-26 / de2472749ad607773c9e00fae039f1df19b6501e
Base main / 18aad0f513b703d74ace32a1cb02f35942a0cbad

Blockers

  • None

Warnings

  • [medium] GitNexus detect changes did not pass: warning.

Validation Commands

  • openspec validate add-one-click-deploy-hybrid
  • C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/tests/test-pr-review-agent.ps1

Checks

  • passed openspec validate add-one-click-deploy-hybrid (openspec)
  • passed script-level PR review agent tests (scripts)

Human Review Notes

  • OpenSpec changes detected: add-one-click-deploy-hybrid
  • Optional AI adapter is not required by policy and was skipped.

@monkey1sai
monkey1sai merged commit 9b756b1 into main May 26, 2026
2 checks passed
@monkey1sai
monkey1sai deleted the docs/one-click-deploy-design-2026-05-26 branch May 26, 2026 10:46

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de2472749a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/deploy.ps1
if (-not $docker.cliVersion) { $hardFails += 'docker_cli_missing' }
if (-not $docker.composeV2) { $hardFails += 'docker_compose_v2_missing' }
if (-not $docker.engineRunning) { $hardFails += 'docker_engine_not_running' }
if (-not $docker.envFile) { $hardFails += 'env_file_missing_entirely' }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor -EnvFile when deciding env-file hard failure

Phase 1 hard-fails on -not $docker.envFile, but $docker.envFile only reflects auto-discovery of .env.web-plane.host-kit{.example} and ignores a valid user-supplied -EnvFile (which is already resolved into $resolvedEnvFile at lines 108-110). As a result, deploy.ps1 -EnvFile <custom-file> can exit with env_file_missing_entirely even when the custom env file exists and should be used, making the advertised custom env-file mode unusable in repos/environments without the default host-kit env filenames.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
Comment on lines +205 to +207
if ($hostNative.venv -eq 'MISSING') {
Write-DeployTag -Tag 'fix' -Message 'creating .venv via python -m venv' -LogPath $LogPath | Out-Null
& python -m venv (Join-Path $RepoRoot '.venv')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip venv bootstrap when host-native stages are disabled

The Phase 2 .venv bootstrap runs unconditionally when hostNative.venv is MISSING, even if both -SkipKit and -SkipConversion are set. In that docker-only mode, missing Python or pip install failures still abort deploy in Phase 2, although no host-native process will be started in Phase 4. This makes the skip flags ineffective for reduced-mode recovery/startup paths and should be gated to only run when at least one host-native stage is enabled.

Useful? React with 👍 / 👎.

Comment on lines +23 to +24
if (-not [int]::TryParse($raw.Trim(), [ref]$procId)) { return $false }
return ($null -ne (& $GetProcessFn $procId))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify pidfile ownership before declaring service running

Test-AlreadyRunning treats any live process ID from *.pid as the target service, without checking process identity. If a stale pidfile survives and that PID is later reused by an unrelated process, Phase 4 will incorrectly skip starting conversion/Kit as “already running,” leaving required services down. This is a real false-positive path on long-lived hosts and should validate executable/command-line (or listening port) before returning true.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
Comment on lines +530 to +531
if (-not (Probe-Url -Name 'coordinator' -Url 'http://127.0.0.1:8004/health')) { $verifyFails += 'coordinator' }
if (-not (Probe-Url -Name 'viewer' -Url 'http://127.0.0.1:5173')) { $verifyFails += 'viewer' }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Probe post-start health on resolved web-plane ports

Phase 5 always verifies coordinator/viewer on 8004 and 5173, but Phase 4c starts containers with ports coming from the selected env file (COORDINATOR_PORT / VIEWER_PORT). When a custom env file changes either port, healthy services are marked unreachable and -StrictPostVerify incorrectly fails deployment with exit 5. The probe targets need to follow the resolved env-derived host ports used by compose.

Useful? React with 👍 / 👎.

)

$dockerPorts = @(8004, 5173)
$hostNativePorts = @(49100, 49101, 47998)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Respect custom Kit ports during port preflight checks

Port preflight is hardcoded to host-native ports 49100 and 47998, even though deploy.ps1 accepts -KitSignalPort and -KitMediaPort and launches Kit with those values. In custom-port runs, Phase 1/3 audit the wrong ports, so real conflicts on the requested Kit ports are missed and startup later fails in Phase 4b instead of being caught and handled by the interactive guard.

Useful? React with 👍 / 👎.

Comment thread scripts/deploy.ps1
Comment on lines +87 to +89
foreach ($pidFile in Get-ChildItem -LiteralPath $RunDir -Filter '*.pid' -ErrorAction SilentlyContinue) {
$procId = (Get-Content $pidFile.FullName | Select-Object -First 1).Trim()
Write-Host " > $($pidFile.BaseName) PID $procId"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter failure summary to only still-running processes

The failure summary prints every scripts\.run\*.pid entry without checking process liveness, so stale pidfiles are reported as if they are still running. This contradicts the summary intent (What might be running) and sends operators toward incorrect cleanup/debug paths after a failed deploy. Re-check each PID before printing it in the failure list.

Useful? React with 👍 / 👎.

monkey1sai added a commit that referenced this pull request May 27, 2026
…ms-spectator-capacity;project-risks-mitigation 保留 active

- 兩個 change 對應實作已在 main (PR #124 hybrid orchestrator、PR #133 LAN viewer CORS + multi spectator handoff、後續 hotfix 9f4d541 / 4752ceb / 8c84fe9),task 9.8 / 4.4 收尾並備註人工驗證來源。
- archive 同步 spec delta:新增 one-click-deploy-hybrid spec,更新 docker-web-plane-host-native-kit / local-coordinator-ifc-ready-intake-boundary / multi-artifact-kit-routing / runtime-verification-task-status。
- project-risks-mitigation 加 Archive Hold 備註,三項原因:5 個 ADDED Requirements 缺 SHALL/MUST 不過 strict validate、對應實作未進 main、2.1/2.2/2.3 follow-up 仍 pending;保留 active 等實作 PR。
- openspec validate --all --strict:31 spec/change 通過,僅 project-risks-mitigation 預期 fail。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants