feat(console): /ui 改掛 EdgeConsole 完整產品操作台(B 方案落地,GPU 為核心前提) - #194
Conversation
把 :8004/ui 從 OperatorConsole(6 頁)換成 EdgeConsole prototype 操作台:今天要做什麼首頁 + 五組導覽(工作台/核心治理/OMNIVERSE/落地端/SYSTEM)+ A1 五步引導 + 逐 A1–A10 的 3D Viewer 呈現頁(ViewerPresentationPage)+ 轉檔/Session/機隊/MinIO 治理頁。基底取用已驗證的 product-governance-console stash,疊上框架修正。
GPU 為核心前提(使用者指正):移除「零 GPU / 無 Omniverse 依賴 / GPU 未取得」等把產品定位成 no-GPU 賣點的字眼;保留 MockViewport「no-GPU=非 live 3D」與 evidence「CPU ~6s」等誠實免責不動。
邊界:MinIO/機隊/Session/轉檔頁全唯讀展示、走 coordinator proxy,無直連 MinIO、無 Kit 控制(restart 明標須送 audited intent 給 Kit Manager)。保留 #/kit、#/demo-control 原 operator-tool 路由(非 silently 砍)。未建一律標 p1/p4/demo。
驗證:vitest 140 pass;npm run build + build:ui green;Playwright 3 e2e pass(live :8004/ui,severeConsole 空);GitNexus impact LOW(isOperatorConsolePath/EdgeConsole)、detect_changes medium 無 HIGH/CRITICAL。證據 artifacts/e2e/pr3-edge-console-{home,a1,viewer}.png + product-governance-console-integration.png + unified-console-{routes,nav}.png。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 38 minutes and 17 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 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis PR migrates the operator console UI from ChangesEdgeConsole Shell & Product Console
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Pull request overview
This PR replaces the OperatorConsole (6-page operator shell) mounted at :8004/ui with a full EdgeConsole product console prototype ("B 方案"). It reframes the GPU narrative from "no-GPU" to "GPU as core premise, dispatched per session," adds a home dashboard, A1–A10 navigation, 3D Viewer presentation page, and multiple governance/runtime management pages — all with honest provenance tagging (asbuilt / p1 / p4 / demo) for unbuilt features. Operator-tool routes (#/kit, #/demo-control) are preserved.
Changes:
- Swapped the top-level console mount from
OperatorConsoletoEdgeConsoleand expanded hash-route recognition from 6 routes to ~28 product routes (backward compatible superset). - Added new page components (
HomePage,A1GovernanceWorkbenchPage,ViewerPresentationPage,ConversionSchedulingPage,SessionManagementPage,KitGpuFleetPage,MinioDataPage,ReportsPage,AdminPage,SpecPage,GpuReviewRoomPage) with five-group navigation, page-aware Copilot prompts, and responsive CSS. - Updated GPU-related wording throughout (removed "零 GPU / 無 Omniverse 依賴" positioning), added
defaultCoordinatorBase()for same-origin API detection when served from coordinator/ui, and updated E2E tests for the new shell.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
web-viewer-sample/src/main.tsx |
Swap OperatorConsole → EdgeConsole import and render |
web-viewer-sample/src/console/routing.ts |
Expand SHORT_CONSOLE_HASH from 6 static routes to 28-route array with dynamic regex |
web-viewer-sample/src/console/pages.tsx |
Add 11 new page components (Home, A1, Viewer, Conversion, Session, Fleet, MinIO, Reports, Admin, Spec, GpuReviewRoom) + helper components |
web-viewer-sample/src/console/EdgeConsole.tsx |
Restructure nav to use NAV_GROUPS, add new route cases in renderBody, add page-aware Copilot prompts, preserve #/kit and #/demo-control |
web-viewer-sample/src/console/edge-console.css |
Add styles for new components (mini-cards, action-cards, tree view, tool trace, responsive breakpoint) |
web-viewer-sample/src/console/data.ts |
Add NAV_GROUPS, expand PAGES to 27 entries with group/badge fields, update GPU wording in comments |
web-viewer-sample/src/console/governanceClient.ts |
Add defaultCoordinatorBase() for same-origin API base URL detection |
web-viewer-sample/src/console/coordinatorClient.ts |
Add identical defaultCoordinatorBase() for same-origin API base URL detection |
web-viewer-sample/src/console/console.test.tsx |
Add SSR smoke tests for new EdgeConsole shell and 6 new page components |
web-viewer-sample/playwright.product-console.config.ts |
New Playwright config targeting coordinator-served /ui bundle |
web-viewer-sample/e2e/unified-console-routes.spec.ts |
Update E2E from OperatorConsole assertions to EdgeConsole (preserve #/kit, #/demo-control checks) |
web-viewer-sample/e2e/product-console-integration.spec.ts |
New E2E spec covering 7 product console pages with console-error capture |
docs/superpowers/plans/2026-06-09-edge-console-product-shell.md |
Implementation plan document for the B-plan product shell |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function defaultCoordinatorBase(): string { | ||
| if (typeof window === "undefined") return "http://127.0.0.1:8004"; | ||
| const { origin, pathname, port } = window.location; | ||
| const devPorts = new Set(["5173", "5174", "5180"]); | ||
| if (pathname.startsWith("/ui") && !devPorts.has(port)) return origin; | ||
| return "http://127.0.0.1:8004"; | ||
| } |
PR Review Agent Summary
Blockers
Warnings
Validation Commands
Checks
Human Review Notes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe2245bfae
ℹ️ 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".
| // Product console E2E targets an already-served coordinator /ui bundle. | ||
| // It intentionally avoids the viewer webServer from playwright.config.ts. | ||
| export default defineConfig({ | ||
| testDir: "./e2e", |
There was a problem hiding this comment.
Limit the product-console Playwright config
With this config, npx playwright test -c playwright.product-console.config.ts --list selects all 14 files under e2e, including viewer harness tests such as viewer-harness.spec.ts that rely on the normal config's baseURL and webServer. Since this product config intentionally omits those, the product-console evidence command will either fail on relative navigations like page.goto("/?harness=1") or run unrelated suites. Please add a testMatch/grep for the product-console routes, or move these specs under a dedicated directory.
Useful? React with 👍 / 👎.
| <Btn caption="governance-service rule-run" prov="asbuilt">執行規則檢核</Btn>{" "} | ||
| <Btn caption="issues from rule-run" prov="asbuilt">失敗構件建 issue</Btn>{" "} | ||
| <Btn caption="BCF / Excel export" prov="asbuilt">匯出 BCF / Excel</Btn>{" "} |
There was a problem hiding this comment.
Wire or disable the A1 action buttons
On the new #/a1 route, these three buttons are enabled and tagged asbuilt, but they have no onClick, so clicking the home-page “跑一次治理檢核” path lands users on an A1 workflow where rule-run, issue creation, and export do nothing. The real handlers already exist on IssuesRuleCenterPage, so these buttons should either navigate there/wire to the same actions or be disabled/marked not built.
Useful? React with 👍 / 👎.
| <Btn caption="browser-visible URL only" prov="asbuilt">Open primary URL</Btn>{" "} | ||
| <Btn caption="browser-visible URL only" prov="asbuilt">Open spectator URL</Btn>{" "} | ||
| <Btn caption="semi-auto allowed for stale spectator" prov="p1">Reclaim stale spectator</Btn>{" "} |
There was a problem hiding this comment.
Wire or disable the session controls
On #/sessions, these controls render as enabled buttons but have no onClick, so the visible primary/spectator open and stale-spectator reclaim actions do nothing when an operator tries to use the session-management page. Since this page is positioned as the ATC control surface and the E2E even asserts the reclaim button is visible, please either connect these to real URLs/actions or render them disabled/clearly not built.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
web-viewer-sample/src/console/pages.tsx (2)
609-609:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix immediate blob URL revocation after download trigger.
The synchronous
URL.revokeObjectURL(a.href)immediately aftera.click()is risky—the browser may not have started reading the blob URL before it's revoked, causing BCF export downloads to fail.Apply the same fix as the Excel export pattern: either use a meaningful delay or don't revoke.
🐛 Proposed fix
a.click(); - URL.revokeObjectURL(a.href); + setTimeout(() => URL.revokeObjectURL(a.href), 1000);Or remove revocation entirely:
a.click(); - URL.revokeObjectURL(a.href);🤖 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 `@web-viewer-sample/src/console/pages.tsx` at line 609, The immediate URL.revokeObjectURL(a.href) after triggering the download is revoking the blob before the browser reads it; in the download flow that contains URL.revokeObjectURL(a.href) update the code to revoke the object URL asynchronously (e.g., use setTimeout(() => URL.revokeObjectURL(a.href), 1000) or attach a load/animation frame callback) or simply remove the revoke call, mirroring the Excel export pattern; locate the download trigger that calls a.click() and change the revoke to a delayed/asynchronous revoke or remove it entirely.
542-543:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winFix blob URL revocation timing for reliable downloads.
The
setTimeout(..., 0)pattern doesn't meaningfully delay the revocation—it only queues the callback for the next tick, which is essentially immediate. For larger Excel files, the browser may not finish reading the blob URL before it's revoked, causing download failures.Replace
setTimeout(..., 0)with either:
- A meaningful delay:
setTimeout(..., 1000)- No revocation (browser cleans up eventually)
- Proper download event listeners (if needed)
🐛 Proposed fix using longer delay
- setTimeout(() => URL.revokeObjectURL(url), 0); + setTimeout(() => URL.revokeObjectURL(url), 1000);Or simply remove the revocation:
a.click(); - // 延後釋放 object URL:同步 revoke 會在瀏覽器開始讀取 blob 前就釋放,導致(尤其較大檔)下載被中止(CodeRabbit)。 - setTimeout(() => URL.revokeObjectURL(url), 0);🤖 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 `@web-viewer-sample/src/console/pages.tsx` around lines 542 - 543, The current revocation uses setTimeout(() => URL.revokeObjectURL(url), 0) which is too soon and can abort large downloads; replace that call so the blob URL is revoked later (e.g., setTimeout with a meaningful delay like 1000 ms) or remove the explicit revocation entirely, or instead attach proper download completion listeners before calling URL.revokeObjectURL(url); update the single occurrence of URL.revokeObjectURL(url) in the download flow where the `url` is created to one of these options.
🧹 Nitpick comments (3)
docs/superpowers/plans/2026-06-09-edge-console-product-shell.md (1)
1-3: ⚡ Quick winAdd explicit docs authority framing and document-type marker.
Please add a short header note that this file is a spec design / implementation plan and that complete agent rules come from sibling
docs/AGENTS.md. This keeps this plan aligned with the docs boundary rules and avoids treating plan text as standalone authority.As per coding guidelines, “Complete rules for agent documentation should be referenced from the sibling
docs/AGENTS.mdfile” and “Mark documentation nature: agent boundary / contract / wiki / runbook / spec design / working note.”🤖 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/plans/2026-06-09-edge-console-product-shell.md` around lines 1 - 3, Add a short header under the existing title indicating this file is a "spec design / implementation plan" and include an explicit document-type marker (e.g., "spec design / implementation plan — agent boundary: refer to docs/AGENTS.md for complete agent rules") plus a sentence that complete agent rules are defined in the sibling docs/AGENTS.md; update the top of the EdgeConsole Product Shell 上線(B 方案 prototype 操作台落地)Implementation Plan document to include that header so readers treat the plan as non-authoritative and follow the AGENTS.md contract.Source: Coding guidelines
web-viewer-sample/src/console/governanceClient.ts (1)
7-13: ⚡ Quick winConsider extracting
defaultCoordinatorBase()to a shared utility.This function is duplicated in both
coordinatorClient.tsandgovernanceClient.ts. Extracting it to a shared module (e.g.,console/utils.ts) would eliminate duplication and ensure consistent behavior.♻️ Refactor suggestion
Create
web-viewer-sample/src/console/utils.ts:export function defaultCoordinatorBase(): string { if (typeof window === "undefined") return "http://127.0.0.1:8004"; const { origin, pathname, port } = window.location; const devPorts = new Set(["5173", "5174", "5180"]); if (pathname.startsWith("/ui") && !devPorts.has(port)) return origin; return "http://127.0.0.1:8004"; }Then import it in both clients:
+import { defaultCoordinatorBase } from "./utils"; + const env = (import.meta as { env?: Record<string, string> }).env; -function defaultCoordinatorBase(): string { - if (typeof window === "undefined") return "http://127.0.0.1:8004"; - const { origin, pathname, port } = window.location; - const devPorts = new Set(["5173", "5174", "5180"]); - if (pathname.startsWith("/ui") && !devPorts.has(port)) return origin; - return "http://127.0.0.1:8004"; -} -🤖 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 `@web-viewer-sample/src/console/governanceClient.ts` around lines 7 - 13, Duplicate defaultCoordinatorBase() exists in coordinatorClient.ts and governanceClient.ts; extract it into a shared utility module (e.g., console/utils.ts) that exports defaultCoordinatorBase, then import and use that function from both coordinatorClient.ts and governanceClient.ts to remove duplication and ensure consistent behavior. Ensure the extracted function preserves the same logic (window check, origin/pathname/port handling, devPorts set, and fallback URL) and update both client files to replace their local implementations with an import of the shared defaultCoordinatorBase.web-viewer-sample/e2e/product-console-integration.spec.ts (1)
8-11: 💤 Low valueConsole error tracking is strict but effective.
The test captures all
console.errorandpageerrorevents and fails if any occur (line 49). This is a strict approach that will catch genuine issues but might cause failures from expected/non-critical errors.If this becomes problematic in CI, consider either:
- Filtering known acceptable error patterns, or
- Using a warning instead of a failure for certain error types.
For now, this strict approach is good for catching regressions early.
Also applies to: 49-49
🤖 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 `@web-viewer-sample/e2e/product-console-integration.spec.ts` around lines 8 - 11, The test currently treats any console.error and pageerror as failures by pushing messages into severeConsole via the page.on("console", ...) and page.on("pageerror", ...) handlers (severeConsole), which can cause flakes from expected non-critical errors; update the handlers to either filter out known acceptable patterns (e.g., match msg.text() or err.message against an allowlist/regex) or record them as warnings instead of failing (e.g., push to a separate warnings array or log via console.warn), keeping the existing severeConsole behavior only for unexpected/unmatched errors; locate the page.on("console", ...) and page.on("pageerror", ...) handlers and implement the allowlist/regex test or alternate warning path in those functions.
🤖 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/plans/2026-06-09-edge-console-product-shell.md`:
- Line 21: The heading "Task 1:建 branch + 套用 stash 基底" is currently at H3 while
the document jumps from H1 to H3 (MD001); change that heading to H2 (i.e.,
prepend one fewer #) or insert an intermediate H2 section before it so the
heading level sequence is H1 → H2 → H3; update the "Task 1:建 branch + 套用 stash
基底" heading accordingly to restore proper Markdown structure.
In `@web-viewer-sample/e2e/product-console-integration.spec.ts`:
- Around line 27-28: Tests use .first() where it’s redundant or fragile; remove
unnecessary .first() calls for exact single matches (e.g., the
getByText("DataChannel") and getByText("highlightPrimsRequest") assertions) and
replace the fragile getByText(/first frame/i).first() with a more specific
locator that targets the intended Field value (e.g., narrow the locator to the
“Open spectator URL” Field or assert existence of that explicit text) so you
stop relying on arbitrary first matches; update the assertions in
product-console-integration.spec.ts to use the exact getByText(...) (no
.first()) for unique strings and a scoped selector for the `/first frame/i`
case.
---
Outside diff comments:
In `@web-viewer-sample/src/console/pages.tsx`:
- Line 609: The immediate URL.revokeObjectURL(a.href) after triggering the
download is revoking the blob before the browser reads it; in the download flow
that contains URL.revokeObjectURL(a.href) update the code to revoke the object
URL asynchronously (e.g., use setTimeout(() => URL.revokeObjectURL(a.href),
1000) or attach a load/animation frame callback) or simply remove the revoke
call, mirroring the Excel export pattern; locate the download trigger that calls
a.click() and change the revoke to a delayed/asynchronous revoke or remove it
entirely.
- Around line 542-543: The current revocation uses setTimeout(() =>
URL.revokeObjectURL(url), 0) which is too soon and can abort large downloads;
replace that call so the blob URL is revoked later (e.g., setTimeout with a
meaningful delay like 1000 ms) or remove the explicit revocation entirely, or
instead attach proper download completion listeners before calling
URL.revokeObjectURL(url); update the single occurrence of
URL.revokeObjectURL(url) in the download flow where the `url` is created to one
of these options.
---
Nitpick comments:
In `@docs/superpowers/plans/2026-06-09-edge-console-product-shell.md`:
- Around line 1-3: Add a short header under the existing title indicating this
file is a "spec design / implementation plan" and include an explicit
document-type marker (e.g., "spec design / implementation plan — agent boundary:
refer to docs/AGENTS.md for complete agent rules") plus a sentence that complete
agent rules are defined in the sibling docs/AGENTS.md; update the top of the
EdgeConsole Product Shell 上線(B 方案 prototype 操作台落地)Implementation Plan document
to include that header so readers treat the plan as non-authoritative and follow
the AGENTS.md contract.
In `@web-viewer-sample/e2e/product-console-integration.spec.ts`:
- Around line 8-11: The test currently treats any console.error and pageerror as
failures by pushing messages into severeConsole via the page.on("console", ...)
and page.on("pageerror", ...) handlers (severeConsole), which can cause flakes
from expected non-critical errors; update the handlers to either filter out
known acceptable patterns (e.g., match msg.text() or err.message against an
allowlist/regex) or record them as warnings instead of failing (e.g., push to a
separate warnings array or log via console.warn), keeping the existing
severeConsole behavior only for unexpected/unmatched errors; locate the
page.on("console", ...) and page.on("pageerror", ...) handlers and implement the
allowlist/regex test or alternate warning path in those functions.
In `@web-viewer-sample/src/console/governanceClient.ts`:
- Around line 7-13: Duplicate defaultCoordinatorBase() exists in
coordinatorClient.ts and governanceClient.ts; extract it into a shared utility
module (e.g., console/utils.ts) that exports defaultCoordinatorBase, then import
and use that function from both coordinatorClient.ts and governanceClient.ts to
remove duplication and ensure consistent behavior. Ensure the extracted function
preserves the same logic (window check, origin/pathname/port handling, devPorts
set, and fallback URL) and update both client files to replace their local
implementations with an import of the shared defaultCoordinatorBase.
🪄 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: 4e79dd01-990a-48ba-9b9c-2e2234e6ffd6
📒 Files selected for processing (13)
docs/superpowers/plans/2026-06-09-edge-console-product-shell.mdweb-viewer-sample/e2e/product-console-integration.spec.tsweb-viewer-sample/e2e/unified-console-routes.spec.tsweb-viewer-sample/playwright.product-console.config.tsweb-viewer-sample/src/console/EdgeConsole.tsxweb-viewer-sample/src/console/console.test.tsxweb-viewer-sample/src/console/coordinatorClient.tsweb-viewer-sample/src/console/data.tsweb-viewer-sample/src/console/edge-console.cssweb-viewer-sample/src/console/governanceClient.tsweb-viewer-sample/src/console/pages.tsxweb-viewer-sample/src/console/routing.tsweb-viewer-sample/src/main.tsx
| await expect(page.locator("main").getByText("DataChannel", { exact: true }).first()).toBeVisible(); | ||
| await expect(page.locator("main").getByText("highlightPrimsRequest", { exact: true }).first()).toBeVisible(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check if the text patterns appear multiple times in the rendered pages
# Search for "DataChannel" in viewer-related components
rg -n -C2 'DataChannel' web-viewer-sample/src/console/pages.tsx
# Search for "mapping coverage" in conversion-related components
rg -n -C2 'mapping coverage' web-viewer-sample/src/console/pages.tsx
# Search for "first frame" in session-related components
rg -n -C2 'first frame' web-viewer-sample/src/console/pages.tsxRepository: monkey1sai/AI-BIM-governance
Length of output: 12668
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/e2e/product-console-integration.spec.ts"
# Show the relevant lines around the reported .first() usage
nl -ba "$FILE" | sed -n '1,120p'
# Locate all getByText usages for the affected strings in this spec file
rg -n 'getByText\("DataChannel"|getByText\("highlightPrimsRequest"' "$FILE"
# Locate all .first() usages in this spec file (to ensure we cover the same lines)
rg -n '\.first\(\)' "$FILE"Repository: monkey1sai/AI-BIM-governance
Length of output: 115
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/e2e/product-console-integration.spec.ts"
# Confirm file exists and show a reasonable slice near the start
ls -la "$FILE"
echo "---- top of file ----"
cat -n "$FILE" | sed -n '1,140p'
# Find the exact assertions mentioned in the original comment
echo "---- DataChannel assertions ----"
rg -n "getByText\\(\"DataChannel\"" "$FILE" -n
echo "---- highlightPrimsRequest assertions ----"
rg -n "getByText\\(\"highlightPrimsRequest\"" "$FILE" -n
# List all .first() in the spec to check whether there are other relevant ones
echo "---- all .first() ----"
rg -n "\\.first\\(" "$FILE"Repository: monkey1sai/AI-BIM-governance
Length of output: 4282
🏁 Script executed:
#!/bin/bash
set -euo pipefail
PAGES="web-viewer-sample/src/console/pages.tsx"
echo "---- occurrences of exact DataChannel label ----"
rg -n 'k="DataChannel"' "$PAGES" -n
rg -n '"DataChannel"' "$PAGES" -n | head -n 80
echo "---- occurrences of highlightPrimsRequest ----"
rg -n 'highlightPrimsRequest' "$PAGES" -n | head -n 120
echo "---- inspect SemanticViewerPage render ----"
rg -n 'function SemanticViewerPage|export function SemanticViewerPage' "$PAGES" -n
# Print a slice around the SemanticViewerPage definition
LINE=$(rg -n 'export function SemanticViewerPage' "$PAGES" | head -n1 | cut -d: -f1)
START=$((LINE-60))
END=$((LINE+260))
sed -n "${START},${END}p" "$PAGES" | cat -n
# Find Field component implementation (so we know what element gets rendered for k="...")
rg -n 'function Field|const Field|export function Field' web-viewer-sample/src/console -S || true
fd -a "Field" web-viewer-sample/src/console | head -n 20 || trueRepository: monkey1sai/AI-BIM-governance
Length of output: 19581
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect Field component rendering
FILE="web-viewer-sample/src/console/components.tsx"
echo "---- Field component ----"
sed -n '1,140p' "$FILE" | cat -n
# 2) Broader search for exact strings across console code
echo "---- all occurrences of literal \"DataChannel\" in web-viewer-sample/src ----"
rg -n '"DataChannel"' web-viewer-sample/src | head -n 200
echo "---- all occurrences of highlightPrimsRequest in web-viewer-sample/src ----"
rg -n 'highlightPrimsRequest' web-viewer-sample/src | head -n 200
# 3) Confirm those strings only appear in the viewer page (best-effort)
echo "---- occurrences within pages.tsx only (context) ----"
rg -n 'DataChannel|highlightPrimsRequest' web-viewer-sample/src/console/pages.tsx | head -n 200Repository: monkey1sai/AI-BIM-governance
Length of output: 10322
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/src/console/pages.tsx"
# show around the highlighted area in the earlier rg output
echo "---- around viewer command matrix (expected highlightPrimsRequest exact label) ----"
sed -n '200,310p' "$FILE" | cat -n
echo "---- around Field k=\"DataChannel\" ----"
sed -n '220,270p' "$FILE" | cat -n
echo "---- around viewer page component where Field/DataChannel is used ----"
# Try to locate the viewer page export by searching for "/viewer" or viewer page title
rg -n '3D Viewer|`#/viewer`|Viewer command matrix|SemanticViewerPage' "$FILE" -n | head -n 50Repository: monkey1sai/AI-BIM-governance
Length of output: 11552
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/src/console/pages.tsx"
start=$(rg -n "export function ViewerPresentationPage" "$FILE" | head -n1 | cut -d: -f1)
end=$(rg -n "export function ConversionSchedulingPage" "$FILE" | head -n1 | cut -d: -f1)
echo "ViewerPresentationPage lines: $start..$((end-1))"
sed -n "${start},$((end-1))p" "$FILE" | cat -n
echo "---- sanity: exact string occurrences within ViewerPresentationPage source ----"
rg -n '"DataChannel"|highlightPrimsRequest' "$FILE" -n | rg -n "($start|$end)" || trueRepository: monkey1sai/AI-BIM-governance
Length of output: 2859
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find where the hash route '`#/viewer`' is wired to ViewerPresentationPage
rg -n "`#/viewer`|\\bviewer\\b.*ViewerPresentationPage|ViewerPresentationPage\\b" web-viewer-sample/src | head -n 200Repository: monkey1sai/AI-BIM-governance
Length of output: 647
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect viewer route rendering
FILE1="web-viewer-sample/src/console/EdgeConsole.tsx"
echo "---- EdgeConsole.tsx ----"
sed -n '1,120p' "$FILE1" | cat -n
echo "---- around viewer switch in EdgeConsole.tsx ----"
rg -n 'case "viewer"|viewer' "$FILE1"
line=$(rg -n 'case "viewer"' "$FILE1" | head -n1 | cut -d: -f1)
start=$((line-40))
end=$((line+40))
sed -n "${start},${end}p" "$FILE1" | cat -n
# Inspect routing.ts if it changes mounted content
FILE2="web-viewer-sample/src/console/routing.ts"
echo "---- routing.ts ----"
sed -n '1,120p' "$FILE2" | cat -nRepository: monkey1sai/AI-BIM-governance
Length of output: 12255
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "49102|governance-service :49102" web-viewer-sample/src web-viewer-sample/e2e || true
rg -n "severeConsole|page\.on\\(\"console\"|msg\.type\\(\\)" web-viewer-sample/e2e/product-console-integration.spec.ts || true
# Also check if any other place has the exact label "governance-service :49102"
rg -n '"governance-service :49102"' . || trueRepository: monkey1sai/AI-BIM-governance
Length of output: 3565
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/src/console/data.ts"
sed -n '80,160p' "$FILE" | cat -n
rg -n '49102|governance-service :49102|port: "127\.0\.0\.1:49102"' "$FILE" -nRepository: monkey1sai/AI-BIM-governance
Length of output: 6408
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/src/console/pages.tsx"
echo "---- exact \"mapping coverage\" occurrences ----"
rg -n '"mapping coverage"' "$FILE" || true
echo "---- exact \"1 GPU = 1 Kit stream\" occurrences ----"
rg -n '"1 GPU = 1 Kit stream"' "$FILE" || true
echo "---- occurrences of /first frame/i text ----"
rg -n 'first frame' "$FILE" || true
echo "---- viewer command matrix highlightPrimsRequest exact command cell ----"
rg -n '"highlightPrimsRequest"' "$FILE" || true
echo "---- exact \"DataChannel\" occurrences ----"
rg -n '"DataChannel"' "$FILE" || true
# Also check EdgeConsole/global layout might contain same exact strings in main
echo "---- exact string occurrences across web-viewer-sample/src/console (broader) ----"
rg -n '"mapping coverage"|\"1 GPU = 1 Kit stream\"|\"DataChannel\"|first frame|highlightPrimsRequest' web-viewer-sample/src/console | head -n 200Repository: monkey1sai/AI-BIM-governance
Length of output: 2367
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="web-viewer-sample/src/console/pages.tsx"
# Locate component blocks
rg -n "export function SessionManagementPage|export function KitGpuFleetPage" "$FILE"
# Print the whole SessionManagementPage and KitGpuFleetPage sections with line numbers (bounded)
s1=$(rg -n "export function SessionManagementPage" "$FILE" | head -n1 | cut -d: -f1)
e1=$(rg -n "export function .*Page" "$FILE" | awk -F: -v s="$s1" '$1>s{print $1}' | sort -n | head -n1)
s2=$(rg -n "export function KitGpuFleetPage" "$FILE" | head -n1 | cut -d: -f1)
e2=$(rg -n "export function .*Page" "$FILE" | awk -F: -v s="$s2" '$1>s{print $1}' | sort -n | head -n1)
echo "---- SessionManagementPage $s1..$((e1-1)) ----"
sed -n "${s1},$((e1-1))p" "$FILE" | cat -n
echo "---- first frame occurrences inside SessionManagementPage ----"
sed -n "${s1},$((e1-1))p" "$FILE" | rg -n "first frame" || true
echo "---- KitGpuFleetPage $s2..$((e2-1)) ----"
sed -n "${s2},$((e2-1))p" "$FILE" | cat -n
echo "---- exact \"1 GPU = 1 Kit stream\" inside KitGpuFleetPage ----"
sed -n "${s2},$((e2-1))p" "$FILE" | rg -n "\"1 GPU = 1 Kit stream\"" || true
# Also confirm which components contain other .first candidates in the e2e file
cat -n web-viewer-sample/e2e/product-console-integration.spec.ts | sed -n '20,60p'Repository: monkey1sai/AI-BIM-governance
Length of output: 7569
Fix .first() fragility in E2E text assertions (exact matches are safe; /first frame/i needs refinement)
In web-viewer-sample/e2e/product-console-integration.spec.ts:
- Lines 27-28 (
"DataChannel"/"highlightPrimsRequest", both with{ exact: true }): these strings are each rendered once on#/viewer(Field k="DataChannel"and the command-matrixcmdcell), so.first()is effectively redundant here. - Line 32 (
"mapping coverage",{ exact: true }) and line 41 ("1 GPU = 1 Kit stream",{ exact: true }): same story—exact single matches, so.first()doesn’t add safety. - Line 36 (
getByText(/first frame/i).first()on#/sessions):/first frame/ican match multiple nodes (the lead copy and the “Open spectator URL” evidence text). Replace.first()with a more specific selector targeting the intendedField(e.g., the “Open spectator URL” value), or assert presence without picking an arbitrary first match.
🤖 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 `@web-viewer-sample/e2e/product-console-integration.spec.ts` around lines 27 -
28, Tests use .first() where it’s redundant or fragile; remove unnecessary
.first() calls for exact single matches (e.g., the getByText("DataChannel") and
getByText("highlightPrimsRequest") assertions) and replace the fragile
getByText(/first frame/i).first() with a more specific locator that targets the
intended Field value (e.g., narrow the locator to the “Open spectator URL” Field
or assert existence of that explicit text) so you stop relying on arbitrary
first matches; update the assertions in product-console-integration.spec.ts to
use the exact getByText(...) (no .first()) for unique strings and a scoped
selector for the `/first frame/i` case.
…oordinatorBase DRY A1 三顆 asbuilt 按鈕原本 enabled 但無 onClick(假按鈕,違反誠實鐵律)→ 改為 onClick 導向 #/issues(真實 Rule Center 執行 rule-run/建 issue/匯出 BCF)。 Session 管理「Controlled actions」原 3 顆 enabled 無 onClick → 全改 disabled + Phase 1 read-only caption(對齊 ATC Tower 既有唯讀模式);E2E 仍斷言按鈕可見故不破。 playwright.product-console.config 加 testMatch 僅收 product-console-integration / unified-console-routes(不誤跑需 webServer 的 viewer-harness)。defaultCoordinatorBase 抽到 coordinatorBase.ts 共用(消 coordinatorClient/governanceClient 重複,回應 Copilot DRY)。plan.md H3→H2(MD001)。 驗證:vitest 140 pass、build green、build:ui + coordinator 重啟(bundle hash DuDENF0U)、Playwright 3 e2e pass(testMatch 生效,僅 2 spec)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR Review Agent Summary
Blockers
Warnings
Validation Commands
Checks
Human Review Notes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2bb6e895eb
ℹ️ 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".
| const useOperatorConsole = isOperatorConsolePath(window.location.pathname, window.location.hash, window.location.search); | ||
| ReactDOM.createRoot(document.getElementById("root")!).render( | ||
| useOperatorConsole ? <OperatorConsole /> : <App /> | ||
| useOperatorConsole ? <EdgeConsole /> : <App /> |
There was a problem hiding this comment.
Preserve the intake selection route
When this switches every /ui/console route to EdgeConsole, the existing #/intake operator workflow stops rendering IntakeSelectPage: OperatorConsole still maps intake to the tested “選取現成模型” page with radio selection and the safe 開啟審查 viewer action, but EdgeConsole maps the same hash to the read-only IntakePage. In the #/intake context operators can no longer select a ready IFC job and open its viewer unless they know another route, so please route intake to IntakeSelectPage or add the same open-viewer action before replacing the mounted console.
Useful? React with 👍 / 👎.
…nonical specs (#197) 於 worktree `.worktrees/openspec-converge-2026-06-09` 一次收斂 8 個 active OpenSpec change(對應已 merged 的 PR #182–#186 / #194 / #195),archive 至 `archive/2026-06-09-<id>/` 並把 spec delta 聯集併入 live canonical specs。 archive = 不可變歷史快照(delta 原樣保留,git 偵測為 R100 純改名); canonical = 聯集 + 跨 change 矛盾調和(沿用 2026-06-03 批次歸檔之收斂規約)。 收斂的 8 個 change: - unified-console-mvp (#182) - console-mapping-proxy (#183) - unified-console-fe-redesign (#184) - unified-console-semantic-viewer (#184/#186) - unified-console-issues-tab (#185) - console-live-semantic-coexist (#186) - product-governance-console-integration (#194;原 untracked,本次一併納入追蹤) - docs-design-spec-source-of-truth (#195) Canonical spec 同步: - `unified-governance-console`:5 → 21 requirements。新增 16 條 ugc delta requirement(mvp 1 / mapping-proxy 1 / fe-redesign 3 / semantic-viewer 3 / issues-tab 1 / live-coexist 2 / product-governance 5,逐條 byte-verbatim)。 - 跨 change 矛盾調和(operator console 掛載點):原 Req「operator 頁分離於三條 /console 獨立路由、A1–A10 overlay-only」(unified-console-mvp MVP 切片)經 fe-redesign(:8004/ui 六 hash 路由 + RK6 + CONSOLE_DIST_DIR)與 product-governance(PR #194 :8004/ui 改掛 EdgeConsole)演進,調和為 「:8004/ui EdgeConsole 產品操作台 shell,A1–A10 既為 console 頁亦為 viewer overlay 操作面」;fe-redesign「React UnifiedConsole 六路由」req 吸收進此調和 req (六路由 / RK6 / CONSOLE_DIST_DIR fallback / ?session 讓位 逐條保留),舊措辭標 superseded(保留歷史脈絡 blockquote)。 - 其餘調和:issues-tab 全幅「問題」分頁加 Req1 carve-out(同一 viewer 非互斥殼); semantic-viewer「中央切 <video>」與 live-coexist「語意側欄並存」coherent(中央 video + 左側欄不覆蓋);mvp req 的 :49100 ban 補「治理/資料 API 目的」scope(與 既有 :8004-only carve-out 一致,自洽於同 req 後句的 WebRTC DataChannel 著色)。 - `docker-web-plane-host-native-kit`:6 → 7(新增 product-governance 的 「Rebuild and E2E Evidence for Product Console」)。 - `documentation-source-of-truth` / `demo-fast-mvp-orchestration` / `agent-doc-context-budget`:docs-design 的 delta 已於 #195/#196 併入 canonical, 本次 archive-only(canonical 為 superset,不重複套用,避免污染)。 未完成 task 驗證分類(誠實鐵律;本批無新 runtime/E2E evidence): - verify-blocked-tooling(OpenSpec CLI 本機不可用:`npx openspec` 回 "could not determine executable to run"):console-mapping-proxy 4.1、 console-live-semantic-coexist 4.1、docs-design 4.6/4.7。改用結構驗證並通過 (21/7 requirement、每 req 有 scenario、無殘留 delta header、diff --check clean)。 - requires-runtime-not-observed(需 host-native Kit/GPU + scripts/deploy.ps1 + live browser 3D,headless 無法觀測,未捏造):unified-console-mvp 5.2、 console-mapping-proxy 4.3、unified-console-fe-redesign 5.3(streaming-server source_client_id 後端強制為明示 Non-goal/待補)。 - deferred-followup(明示後續 PR):unified-console-semantic-viewer 1.1/1.5/3.4 (完整 GovViewerLayout 重構 / orbit-pan-zoom DataChannel / CH-H1b 完整版面)。 - verify-pass:console-live-semantic-coexist 4.2(PR/CI/merge 已成,archive/sync 即本 commit)、unified-console-semantic-viewer 3.2(CH-H2 測試檔已落地)。 - 無 task 屬「與 repo 不一致需省略」——8 個 change implementation 皆已在 main 且 一致(triage 8 agent 逐一 code spot-check 確認)。 對抗驗證(3 agent 平行): - completeness:0 dropped;17 delta + 4 pre-existing = 21,math closes。 - contradiction:coherent;4 reconciliation 全過,1 minor :49100 wording 已修。 - structure/immutability/git:clean;24×R100 rename(delta 不可變)+ product-governance diff -r exit 0;active changes 僅剩 archive/。 GitNexus detect_changes:N/A——本變更僅動 openspec/ markdown spec(無 code symbol/flow),且 detect_changes 對 linked worktree staged 為盲(見既有 closeout note)。 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-1 NVIDIA callout) (#200) 補入先前 superpowers 工作產出但未追蹤的 2 份 plan 文件至 docs/superpowers/plans/: - 2026-06-08-product-governance-console-integration.md(對應已 merged PR #194 的實作計畫) - 2026-06-09-pr1-runtime-nvidia-constraints-callout.md(對應 #192 NVIDIA OVAS 約束 callout 計畫) 純文件,與 docs/superpowers/plans/ 既有 15 份計畫同類。未碰 gitignored 的 .superpowers/ 本機 scratch。 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
目的
依使用者定案「B 方案」:把
:8004/ui從OperatorConsole(6 頁 operator)換成 EdgeConsole 完整 prototype 產品操作台,並把 GPU 修正為核心前提(使用者明確指正:repo 以有 GPU 為前提開發,不可框架成 no-GPU 賣點)。對齊使用者三點要求:
ViewerPresentationPage(#/viewer),逐一說明各 A 在 3D viewer 的用途、command matrix、first-frame / stage-truth 證據要求。model.usdc、Kit/GPU 遙測一律標p1 / p4 / demo,不做假按鈕。主要變更
main.tsx:isOperatorConsolePath命中時改掛EdgeConsole(原OperatorConsole)。routing.ts:短 hash 從 6 路由擴成 product 路由表(home/a1..a10/viewer/conv/sessions/instances/minio/...),向後相容舊路由。EdgeConsole.tsx/pages.tsx/data.ts/edge-console.css:prototype 殼層 + 新頁(Home / A1 / Viewer / Conversion / Session / KitGpuFleet / Minio / Reports / Admin / Spec / GpuReviewRoom)。MockViewport「no-GPU=非 live 3D」與 evidence「CPU ~6s」等誠實免責不動。#/kit(KitConsolePage)、#/demo-control(RealIfcConsolePage,真實 IFC E2E 入口) 在 EdgeConsole renderBody 保留,非 silently 砍。邊界(已審,全唯讀)
coordinatorproxy(/api/runtime/status、/api/external/ifc-ready),危險動作 disabled + 標 p1。Frontend Verification
http://127.0.0.1:8004/ui/ui→今天要做什麼首頁;#/a1→五步引導;#/viewer→3D Viewer 呈現;#/kit/#/demo-control保留可達artifacts/e2e/pr3-edge-console-{home,a1,viewer}.png、product-governance-console-integration.png、unified-console-{routes,nav}.png四套工具管線
docs/superpowers/plans/2026-06-09-edge-console-product-shell.md。isOperatorConsolePath/EdgeConsole皆 LOW;detect_changes medium(10 檔、RenderBody→ProvTag/Col,無 HIGH/CRITICAL)。npm run build+build:uigreen、dist-ui 重建 + coordinator 重啟(bundle hash MATCH)。🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests
Documentation