From 7fdd452014271a188772b82e1c7e012f1f7b0653 Mon Sep 17 00:00:00 2001 From: "Yifeng[Terry] Yu" <125581657+xiaojiou176@users.noreply.github.com> Date: Mon, 13 Apr 2026 07:00:35 -0700 Subject: [PATCH] feat: close OpenVibeCoding shell on signed branch --- README.md | 81 ++-- apps/dashboard/README.md | 6 + apps/dashboard/app/agents/page.tsx | 151 +++--- apps/dashboard/app/command-tower/page.tsx | 4 +- apps/dashboard/app/contracts/page.tsx | 304 +++++++++--- apps/dashboard/app/globals.tokens.css | 179 +++---- apps/dashboard/app/layout.tsx | 4 +- apps/dashboard/app/page.tsx | 31 +- apps/dashboard/app/planner/page.tsx | 453 ++++++++++++++++++ apps/dashboard/app/runs/[id]/compare/page.tsx | 42 +- apps/dashboard/app/runs/page.tsx | 48 +- apps/dashboard/app/workflows/[id]/page.tsx | 6 +- apps/dashboard/app/workflows/page.tsx | 5 +- apps/dashboard/components/AppNav.tsx | 7 + .../components/DashboardHomeStorySections.tsx | 351 ++++++++++---- apps/dashboard/package.json | 2 +- ...ts_page_pagination_accessibility.suite.tsx | 2 +- ...command_tower_page_ssr_query_repro.test.ts | 2 +- .../dashboard/tests/command_tower_ui.test.tsx | 2 +- apps/dashboard/tests/home_page.test.tsx | 27 +- apps/dashboard/tests/planner_page.test.tsx | 112 +++++ .../dashboard/tests/run_compare_page.test.tsx | 1 + apps/dashboard/tests/run_detail_page.test.tsx | 10 +- apps/dashboard/tests/runlist.test.tsx | 8 +- .../tests/workflow_detail_page.test.tsx | 4 +- .../tests/workflows_queue_page.test.tsx | 2 +- apps/desktop/index.html | 2 +- apps/desktop/src/App.test.tsx | 20 +- .../src/components/layout/AppSidebar.tsx | 11 +- .../src/features/pm-shell/desktopPages.tsx | 6 +- apps/desktop/src/pages/AgentsPage.test.tsx | 4 +- apps/desktop/src/pages/AgentsPage.tsx | 123 ++++- apps/desktop/src/pages/CommandTowerPage.tsx | 8 +- apps/desktop/src/pages/ContractsPage.test.tsx | 20 +- apps/desktop/src/pages/ContractsPage.tsx | 200 +++++++- apps/desktop/src/pages/OverviewPage.tsx | 65 ++- apps/desktop/src/pages/RunComparePage.tsx | 39 +- apps/desktop/src/pages/WorkflowDetailPage.tsx | 56 +-- .../pages/desktop_p0_misc_controls.test.tsx | 4 +- .../src/pages/desktop_p1_controls.test.tsx | 6 +- .../pages/overview_policies_branch.test.tsx | 14 +- .../pages/run_compare_page_decision.test.tsx | 1 + apps/desktop/src/styles.tokens.css | 246 +++------- .../cortexpilot_orch/runners/agents_runner.py | 2 +- .../runners/app_server_runner.py | 2 +- .../cortexpilot_orch/runners/codex_runner.py | 2 +- .../src/cortexpilot_orch/runners/common.py | 68 ++- .../runners/langgraph_contract_subflow.py | 4 +- .../scheduler/scheduler_bridge_finalize.py | 86 ++++ .../tests/test_coverage_boost_misc.py | 28 +- .../test_scheduler_bridge_finalize_edges.py | 25 + docs/README.md | 8 +- docs/agent-starters/index.html | 28 +- docs/ai-surfaces/index.html | 28 +- docs/api/index.html | 24 +- docs/architecture/runtime-topology.md | 5 + docs/builders/index.html | 16 +- docs/compatibility/index.html | 40 +- docs/distribution/index.html | 20 +- docs/ecosystem/index.html | 30 +- docs/index.html | 54 ++- docs/integrations/index.html | 68 +-- docs/mcp/index.html | 24 +- docs/skills/index.html | 44 +- docs/specs/00_SPEC.md | 4 + docs/use-cases/index.html | 14 +- packages/frontend-shared/uiCopy.js | 84 ++-- packages/frontend-shared/uiCopy.ts | 117 ++--- 68 files changed, 2461 insertions(+), 1033 deletions(-) create mode 100644 apps/dashboard/app/planner/page.tsx create mode 100644 apps/dashboard/tests/planner_page.test.tsx diff --git a/README.md b/README.md index 9126d8c2..6bba7668 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,30 @@ -# CortexPilot +# OpenVibeCoding -The command tower for AI engineering. +The open command tower for AI engineering. -Stop babysitting AI coding work. CortexPilot helps teams plan, delegate, track, -resume, and prove long-running engineering work across Codex and Claude Code -instead of juggling scattered chats, local scripts, and after-the-fact logs. +Stop babysitting AI coding work. + +AI coding does not lack models. It lacks a command tower. + +OpenVibeCoding is the public shell for the CortexPilot repo and runtime +compatibility layer. It helps teams plan / delegate / track / resume / prove +long-running engineering work across Codex and Claude Code instead of +juggling scattered chats, local scripts, and after-the-fact logs. CortexPilot is a contract-first multi-agent orchestration repository. +The underlying repository, runtime, package, and compatibility names remain +`CortexPilot` where that internal surface is the truthful identifier. + The public story is intentionally narrower than the full monorepo: - **See one proven workflow first** - **Choose the right adoption path second** - **Open MCP / API / builder / skills surfaces only after the real job is clear** -Current public boundary: CortexPilot is a repo-backed AI engineering command -tower, not a hosted product, and the shipped MCP surface remains **read-only**. +Current public boundary: OpenVibeCoding is a repo-backed public shell over the +CortexPilot runtime, not a hosted product, and the shipped MCP surface remains +**read-only**. Current lane order is deliberate: @@ -31,7 +40,14 @@ Current lane order is deliberate: The shortest truthful answer today is: -> CortexPilot officially ships a public repo, a public Pages front door, a repo-local read-only MCP surface, a published PyPI package, a live Official MCP Registry entry, and a live ClawHub skill. The adoption-router skill is the secondary public lane. Local coding-agent starters and bundle examples remain companion/example materials, not the canonical public root. OpenHands/extensions and MCP.so external receipts exist, while hosted service, write-capable MCP, Docker distribution, and standalone npm releases remain deferred. +> OpenVibeCoding currently ships through the public CortexPilot repo, a public +> Pages front door, a repo-local read-only MCP surface, a published PyPI +> package, a live Official MCP Registry entry, and a live ClawHub skill. The +> adoption-router skill is the secondary public lane. Local coding-agent +> starters and bundle examples remain companion/example materials, not the +> canonical public root. OpenHands/extensions and MCP.so external receipts +> exist, while hosted service, write-capable MCP, Docker distribution, and +> standalone npm releases remain deferred. Use these buckets: @@ -99,11 +115,11 @@ release, new task templates, and storefront updates. If you need contributor setup instead of product evaluation, jump to the [30-minute onboarding guide](docs/runbooks/onboarding-30min.md). -## Why CortexPilot Exists +## Why OpenVibeCoding Exists -Most agent demos stop at "the model replied." CortexPilot is built for the next -question: **can we inspect what happened, review what changed, classify the -workflow case, and rerun it without guessing?** +Most agent demos stop at "the model replied." OpenVibeCoding is built for the +next question: **can we inspect what happened, review what changed, classify +the workflow case, and rerun it without guessing?** The deeper product claim is straightforward: @@ -119,7 +135,7 @@ The engineering philosophy underneath that loop is equally explicit: - **Context Engineering**: keep the right material in the right head, and treat explicit handoff as a fallback rather than the default loop. - **Harness Engineering**: move work through contracts, runtime bindings, approvals, and proof surfaces so the system can keep operating safely. -This repository combines: +The CortexPilot runtime underneath that public shell combines: - **Command Tower**: one operator surface for governed AI work, live run visibility, queue posture, and L0-style oversight - **Workflow Cases**: one stable operating record that ties request, verdict, proof, and linked runs together @@ -189,7 +205,7 @@ If the first success path fails, go here next: ## The First Loop -The clearest way to understand CortexPilot is: +The clearest way to understand OpenVibeCoding is: 1. **PM**: describe the task and acceptance target 2. **Workflow Case**: confirm the case identity, queue state, and operating verdict @@ -214,9 +230,10 @@ repository should be judged on. `Switchyard /v1/runtime/invoke` for intake/operator-style chat paths, but MCP tool execution still needs a tool-capable provider path and therefore fails closed instead of pretending Switchyard already has tool parity -- CortexPilot is still **not** a hosted operator service; `cortexpilot.ai` - should be treated as a marketing/holding domain until the public contract, - support boundary, and live surface materially change +- the CortexPilot runtime under OpenVibeCoding is still **not** a hosted + operator service; `cortexpilot.ai` should be treated as a marketing/holding + domain until the public contract, support boundary, and live surface + materially change ## Public CI Safety Model @@ -322,7 +339,7 @@ release-proven until they have their own healthy proof and benchmark artifacts. Use these names as ecosystem anchors, not as co-brands or partnership claims. -- **Codex**: primary workflow audience; CortexPilot is built for governed +- **Codex**: primary workflow audience; OpenVibeCoding is built for governed Codex-style coding runs that need cases, approvals, and replayable proof. - **Claude Code**: primary workflow audience alongside Codex; the same Command Tower / Workflow Case / Proof & Replay spine applies. @@ -340,7 +357,7 @@ Use these names as ecosystem anchors, not as co-brands or partnership claims. ## Official Ecosystem Anchors When a team asks "what is real on their side?", start from the native surfaces -below before you explain where CortexPilot fits: +below before you explain where OpenVibeCoding fits: - **Codex**: - repo: [openai/codex](https://github.com/openai/codex) @@ -359,21 +376,21 @@ below before you explain where CortexPilot fits: - skills docs: [docs.openclaw.ai/tools/skills](https://docs.openclaw.ai/tools/skills) - registry/catalog: [openclaw/clawhub](https://github.com/openclaw/clawhub) -These anchors matter because CortexPilot should fit around the real ecosystem +These anchors matter because OpenVibeCoding should fit around the real ecosystem surfaces that already exist: - **Codex**: Codex now has real plugin surfaces of its own, including local - marketplace installs and a curated official directory. CortexPilot should sit + marketplace installs and a curated official directory. OpenVibeCoding should sit around Codex workflows with command tower, proof, replay, read-only MCP, and repo-owned skills or local bundle examples until a real published listing exists. - **Claude Code**: Claude Code's current native surfaces include plugins, MCP, - hooks, subagents, and project configuration. CortexPilot should wrap those + hooks, subagents, and project configuration. OpenVibeCoding should wrap those governed workflows with command tower, proof, replay, read-only MCP, and - repo-owned starter kits rather than pretending a published CortexPilot + repo-owned starter kits rather than pretending a published OpenVibeCoding marketplace listing already exists. - **OpenClaw**: adjacent integration layer with real skills and plugin/catalog - surfaces on its side, while CortexPilot stays on the review/proof/read-only + surfaces on its side, while OpenVibeCoding stays on the review/proof/read-only integration side unless a mapped native path is explicitly shipped and tested. @@ -387,13 +404,13 @@ The strongest public loop is now: 3. Reuse the Workflow Case as a **share-ready recap asset** instead of keeping it trapped inside a single operator page. -That turns CortexPilot from “a repo you can run” into “a repo you can show, +That turns OpenVibeCoding from “a repo you can run” into “a repo you can show, review, and hand off.” ## Builder Entry Points These are the current public-facing entry points for teams that want to build -around CortexPilot without pretending a full SDK platform already exists: +around OpenVibeCoding without pretending a full SDK platform already exists: - [packages/frontend-api-client/README.md](packages/frontend-api-client/README.md): thin JavaScript/TypeScript client entry points for dashboard, desktop, and web surfaces, including the repo-owned `createControlPlaneStarter(...)` bootstrap path for overview + agents + contracts + role-config integration. - [packages/frontend-api-contract/docs/README.md](packages/frontend-api-contract/docs/README.md): human-readable contract package guide for generated frontend-safe route/query/type imports. @@ -413,7 +430,7 @@ If your team needs starter assets instead of only wording, open: ## Shortest Cross-Ecosystem Adoption Order -If you are integrating CortexPilot into a coding-agent workflow, the shortest +If you are integrating OpenVibeCoding into a coding-agent workflow, the shortest truthful order is: 1. Confirm the native ecosystem surface first: @@ -421,8 +438,8 @@ truthful order is: - Claude Code overview / MCP - OpenClaw repo / skills / ClawHub 2. Use the public [compatibility matrix](https://xiaojiou176-open.github.io/CortexPilot-public/compatibility/) - to choose the right CortexPilot entrypoint. -3. Pick the first CortexPilot lane based on the job: + to choose the right OpenVibeCoding entrypoint. +3. Pick the first OpenVibeCoding lane based on the job: - [read-only MCP](https://xiaojiou176-open.github.io/CortexPilot-public/mcp/) for protocol inspection - [skills quickstart](https://xiaojiou176-open.github.io/CortexPilot-public/skills/) @@ -445,7 +462,7 @@ truthful order is: ## Best Fit -CortexPilot is a strong fit if you are building or evaluating: +OpenVibeCoding is a strong fit if you are building or evaluating: - agent workflows that need **reviewable evidence** - orchestration systems that need **replay / re-exec** @@ -454,7 +471,7 @@ CortexPilot is a strong fit if you are building or evaluating: ## Not A Fit -CortexPilot is not the right choice if you want: +OpenVibeCoding is not the right choice if you want: - a polished hosted SaaS product - write-capable agent control-plane mutations through MCP today @@ -488,7 +505,7 @@ The current stage freeze keeps two high-risk directions explicitly constrained: - **Hosted operator surface** remains **No-Go**. - `cortexpilot.ai` is still a weak marketing/holding domain, not a production front door. -- The current public contract still describes CortexPilot as source code plus +- The current public contract still describes OpenVibeCoding as source code plus operator/demo surfaces, not as a hosted service. - Reopen hosted only if the public boundary, support contract, privacy/security wording, and live front door materially change together. diff --git a/apps/dashboard/README.md b/apps/dashboard/README.md index ae8ea3d4..7124c252 100644 --- a/apps/dashboard/README.md +++ b/apps/dashboard/README.md @@ -42,6 +42,12 @@ repository is already a finished consumer product. - PM workspace: registry-driven task-pack selection plus `execution_plan_report` preview before execution starts. +- Planner desk: a first-class planner-facing triage route that pulls + `planning_wave_plan`, `planning_worker_prompt_contracts`, + `planning_unblock_tasks`, and `completion_governance_report` into one + planning control desk, now with inline queue / dispatch controls so planners + can act on the next contract instead of only reading artifacts and clicking + away to other desks. - Agents: the first-screen role catalog now also hosts a repo-owned role configuration desk for previewing and saving future compiled defaults (`system_prompt_ref`, bundle refs, and role-level runtime binding) while diff --git a/apps/dashboard/app/agents/page.tsx b/apps/dashboard/app/agents/page.tsx index f2e051c6..4b20b4d5 100644 --- a/apps/dashboard/app/agents/page.tsx +++ b/apps/dashboard/app/agents/page.tsx @@ -419,78 +419,6 @@ export default async function AgentsPage({ searchParams }: AgentsPageProps) {

{agentsPageCopy.metricSublines.schedulerHint(lockedAgentCount)}

- -
-
-
-

Role catalog (read-only first screen)

-

{agentsPageCopy.roleCatalog.title}

-

{agentsPageCopy.roleCatalog.subtitle}

-
-
- -
-
- - {payloadWarning ? ( -

{agentsPageCopy.roleCatalog.registryUnavailable}

- ) : roleCatalog.length === 0 ? ( -

{agentsPageCopy.roleCatalog.noMatches}

- ) : ( - - - - - - - - - - - - - {roleCatalog.map((roleEntry) => ( - - - - - - - - - ))} - -
{agentsPageCopy.roleCatalog.headers.role}{agentsPageCopy.roleCatalog.headers.skillsBundle}{agentsPageCopy.roleCatalog.headers.mcpBundle}{agentsPageCopy.roleCatalog.headers.runtimeBinding}{agentsPageCopy.roleCatalog.headers.executionAuthority}{agentsPageCopy.roleCatalog.headers.registeredSeats}
-
- {roleEntry.role} - {roleEntry.purpose || agentsPageCopy.roleCatalog.noRolePurpose} -
-
- - {formatBindingReadModelLabel(roleEntry.role_binding_read_model?.skills_bundle_ref)} - - - - {formatBindingReadModelLabel(roleEntry.role_binding_read_model?.mcp_bundle_ref)} - - {formatRoleBindingRuntimeSummary(roleEntry.role_binding_read_model)} -
- {roleEntry.role_binding_read_model.execution_authority} - {agentsPageCopy.roleCatalog.readOnlyMirror} -
-
-
- {roleEntry.registered_agent_count} - - {roleEntry.locked_agent_count} {agentsPageCopy.roleCatalog.lockedSuffix} - -
-
- )} -
-
@@ -530,7 +458,6 @@ export default async function AgentsPage({ searchParams }: AgentsPageProps) {
- {/* ── Active State Machine ── */}
@@ -788,6 +715,84 @@ export default async function AgentsPage({ searchParams }: AgentsPageProps) {
+ +
+ +
+ + {agentsPageCopy.roleCatalog.title} + +
+
+

{agentsPageCopy.roleCatalog.subtitle}

+
+
+ +
+
+
+ {payloadWarning ? ( +

{agentsPageCopy.roleCatalog.registryUnavailable}

+ ) : roleCatalog.length === 0 ? ( +

{agentsPageCopy.roleCatalog.noMatches}

+ ) : ( + + + + + + + + + + + + + {roleCatalog.map((roleEntry) => ( + + + + + + + + + ))} + +
{agentsPageCopy.roleCatalog.headers.role}{agentsPageCopy.roleCatalog.headers.skillsBundle}{agentsPageCopy.roleCatalog.headers.mcpBundle}{agentsPageCopy.roleCatalog.headers.runtimeBinding}{agentsPageCopy.roleCatalog.headers.executionAuthority}{agentsPageCopy.roleCatalog.headers.registeredSeats}
+
+ {roleEntry.role} + {roleEntry.purpose || agentsPageCopy.roleCatalog.noRolePurpose} +
+
+ + {formatBindingReadModelLabel(roleEntry.role_binding_read_model?.skills_bundle_ref)} + + + + {formatBindingReadModelLabel(roleEntry.role_binding_read_model?.mcp_bundle_ref)} + + {formatRoleBindingRuntimeSummary(roleEntry.role_binding_read_model)} +
+ {roleEntry.role_binding_read_model.execution_authority} + {agentsPageCopy.roleCatalog.readOnlyMirror} +
+
+
+ {roleEntry.registered_agent_count} + + {roleEntry.locked_agent_count} {agentsPageCopy.roleCatalog.lockedSuffix} + +
+
+ )} +
+
+
+
+
diff --git a/apps/dashboard/app/runs/page.tsx b/apps/dashboard/app/runs/page.tsx index 4e21b530..ee10de88 100644 --- a/apps/dashboard/app/runs/page.tsx +++ b/apps/dashboard/app/runs/page.tsx @@ -1,5 +1,6 @@ import { cookies } from "next/headers"; import Link from "next/link"; +import type { Metadata } from "next"; import { getUiCopy } from "@cortexpilot/frontend-shared/uiCopy"; import { normalizeUiLocale, UI_LOCALE_STORAGE_KEY } from "@cortexpilot/frontend-shared/uiLocale"; @@ -15,6 +16,12 @@ type RunsPageProps = { searchParams?: Promise>; }; +export const metadata: Metadata = { + title: "Proof & Replay | OpenVibeCoding", + description: + "Inspect latest outcomes, replay posture, failure clues, and next operator actions from the OpenVibeCoding proof and replay surface.", +}; + function queryValue(value: string | string[] | undefined): string { return Array.isArray(value) ? String(value[0] || "").trim() : String(value || "").trim(); } @@ -77,6 +84,7 @@ export default async function RunsPage({ searchParams }: RunsPageProps) {
+

OpenVibeCoding / proof and replay

{runsPageCopy.title}

{runsPageCopy.subtitle}

@@ -94,26 +102,6 @@ export default async function RunsPage({ searchParams }: RunsPageProps) {

{operatorDeskNote}

-
- - - - -
- {runs.length > visibleRuns.length ? ( -

- {runsPageCopy.firstScreenLimit(visibleRuns.length)} -

- ) : null} -
@@ -147,6 +135,26 @@ export default async function RunsPage({ searchParams }: RunsPageProps) {
+
+ + + + +
+ {runs.length > visibleRuns.length ? ( +

+ {runsPageCopy.firstScreenLimit(visibleRuns.length)} +

+ ) : null} + ); diff --git a/apps/dashboard/app/workflows/[id]/page.tsx b/apps/dashboard/app/workflows/[id]/page.tsx index 3834bdc3..57309a2a 100644 --- a/apps/dashboard/app/workflows/[id]/page.tsx +++ b/apps/dashboard/app/workflows/[id]/page.tsx @@ -84,9 +84,9 @@ export async function generateMetadata({ const workflowId = safeDecodeParam(id); const titleSuffix = workflowId ? ` · ${workflowId}` : ""; return { - title: `Workflow Case detail${titleSuffix} | CortexPilot`, + title: `Workflow Case detail${titleSuffix} | OpenVibeCoding`, description: - "Inspect one Workflow Case across risk, queue posture, linked runs, event timeline, and the next operator action inside CortexPilot.", + "Inspect one Workflow Case across risk, queue posture, linked runs, event timeline, and the next operator action inside the OpenVibeCoding command tower.", }; } @@ -143,6 +143,7 @@ export default async function WorkflowDetailPage({
+

OpenVibeCoding / workflow case detail

{workflowDetailPageCopy.title}

{workflowDetailPageCopy.subtitle}

@@ -212,6 +213,7 @@ export default async function WorkflowDetailPage({
+

OpenVibeCoding / workflow case detail

{workflowDetailPageCopy.title}

{workflowDetailPageCopy.subtitle}

diff --git a/apps/dashboard/app/workflows/page.tsx b/apps/dashboard/app/workflows/page.tsx index 4cf44a05..0d095273 100644 --- a/apps/dashboard/app/workflows/page.tsx +++ b/apps/dashboard/app/workflows/page.tsx @@ -19,9 +19,9 @@ import { import WorkflowQueueMutationControls from "./WorkflowQueueMutationControls"; export const metadata: Metadata = { - title: "Workflow Cases | CortexPilot", + title: "Workflow Cases | OpenVibeCoding", description: - "Review Workflow Cases, queue posture, linked runs, and next operator actions inside the CortexPilot Command Tower.", + "Review Workflow Cases, queue posture, linked runs, and next operator actions inside the OpenVibeCoding command tower.", }; function statusBadgeVariant(status: string | undefined): BadgeVariant { @@ -174,6 +174,7 @@ export default async function WorkflowsPage() {
+

OpenVibeCoding / workflow desk

{workflowListPageCopy.title}

{workflowListPageCopy.subtitle}

diff --git a/apps/dashboard/components/AppNav.tsx b/apps/dashboard/components/AppNav.tsx index 25c16bc1..820235ae 100644 --- a/apps/dashboard/components/AppNav.tsx +++ b/apps/dashboard/components/AppNav.tsx @@ -25,6 +25,7 @@ function buildNavSections(locale: UiLocale): NavSection[] { items: [ { href: "/", label: uiCopy.dashboard.labels.overview, icon: "grid" }, { href: "/pm", label: uiCopy.dashboard.labels.pmIntake, icon: "message" }, + { href: "/planner", label: uiCopy.dashboard.labels.planner, icon: "map" }, { href: "/command-tower", label: uiCopy.dashboard.labels.commandTower, icon: "tower" }, { href: "/workflows", label: uiCopy.dashboard.labels.workflowCases, icon: "workflow" }, { href: "/runs", label: uiCopy.dashboard.labels.runs, icon: "play" }, @@ -82,6 +83,12 @@ const ICON_MAP: Record = { ), + map: ( + + + + + ), activity: ( diff --git a/apps/dashboard/components/DashboardHomeStorySections.tsx b/apps/dashboard/components/DashboardHomeStorySections.tsx index 121ccadd..c0d49734 100644 --- a/apps/dashboard/components/DashboardHomeStorySections.tsx +++ b/apps/dashboard/components/DashboardHomeStorySections.tsx @@ -13,6 +13,7 @@ type DashboardHomeStorySectionsProps = { hasRunHistory: boolean; latestFailureGovernanceHref: string; locale: UiLocale; + runningCount: number; showFirstTaskGuide: boolean; }; @@ -22,6 +23,7 @@ export default function DashboardHomeStorySections({ hasRunHistory, latestFailureGovernanceHref, locale, + runningCount, showFirstTaskGuide, }: DashboardHomeStorySectionsProps) { const { locale: dashboardLocale, uiCopy } = useDashboardLocale(); @@ -29,6 +31,60 @@ export default function DashboardHomeStorySections({ dashboardLocale === DEFAULT_UI_LOCALE && locale !== DEFAULT_UI_LOCALE ? getUiCopy(locale) : uiCopy; const homePhase2Copy = resolvedUiCopy.dashboard.homePhase2; const resolveHomeHref = (href: string) => resolveDashboardPublicDocsHref(href); + const shellCopy = + locale === "zh-CN" + ? { + eyebrow: "OpenVibeCoding / 指挥塔入口", + heroTitle: homePhase2Copy.heroTitle, + heroSubtitle: homePhase2Copy.heroSubtitle, + primaryAction: hasRunHistory ? "打开 Command Tower" : homePhase2Copy.startFirstTaskLabel, + secondaryAction: hasRunHistory ? homePhase2Copy.startNewTaskLabel : homePhase2Copy.viewLatestRunsLabel, + deskTitle: "第一屏控制台", + deskDescription: + "把最重要的操作面放到第一排,不再让首页像等权链接目录。", + loopTitle: "Operator loop", + loopDescription: + "先 plan,再 delegate,然后 track、resume、prove。首页应该像飞行前简报,不是站内导航页。", + methodTitle: "方法层,不抢第一屏", + methodDescription: + "Prompt / Context / Harness 仍然重要,但它们应该在 command tower 语义之后出现。", + templatesTitle: "任务包与起步模版", + templatesDescription: + "这些是起步工具,不是首页主角。先看 cockpit,再决定拿哪套模版。", + adoptionTitle: "延伸入口", + adoptionDescription: + "公共文档与生态入口保留在后排,避免抢走 command tower 的第一印象。", + guidesTitle: "第二层导览,不抢首屏", + guidesDescription: + "方法层、模板层和生态入口保留,但收进更安静的第二层,避免首页继续像站点目录墙。", + guidesSummary: "展开第二层导览", + } + : { + eyebrow: "OpenVibeCoding / command tower entry", + heroTitle: homePhase2Copy.heroTitle, + heroSubtitle: homePhase2Copy.heroSubtitle, + primaryAction: hasRunHistory ? "Open command tower" : homePhase2Copy.startFirstTaskLabel, + secondaryAction: hasRunHistory ? homePhase2Copy.startNewTaskLabel : homePhase2Copy.viewLatestRunsLabel, + deskTitle: "First-screen control desk", + deskDescription: + "Put the highest-leverage surfaces in the front row so the page reads like a cockpit, not a catalog of equal-weight links.", + loopTitle: "Operator loop", + loopDescription: + "Plan, delegate, track, resume, and prove. The home page should behave like a pre-flight briefing, not a router-heavy portal.", + methodTitle: "Method layer, not the hero", + methodDescription: + "Prompt / Context / Harness still matters, but it belongs below the control loop instead of competing with it.", + templatesTitle: "Task packs and launch templates", + templatesDescription: + "Templates stay available, but they should not outrank the command tower on the first screen.", + adoptionTitle: "Extended surfaces", + adoptionDescription: + "Public docs and ecosystem entry points stay in the second layer so the command tower remains the first impression.", + guidesTitle: "Second-layer guides, not the first impression", + guidesDescription: + "Keep methods, templates, and ecosystem routes available, but move them into a calmer second layer so the home page stops reading like a route catalog.", + guidesSummary: "Open second-layer guides", + }; const adoptionCards = [ homePhase2Copy.integrationCards[0], homePhase2Copy.builderCards[0], @@ -36,10 +92,109 @@ export default function DashboardHomeStorySections({ homePhase2Copy.integrationCards[2], homePhase2Copy.builderCards[1], ]; + const controlDeskCards = [ + { + href: "/command-tower", + badge: locale === "zh-CN" ? "Where am I" : "Where am I", + title: locale === "zh-CN" ? "实时 Command Tower" : "Live command tower", + desc: hasRunHistory + ? locale === "zh-CN" + ? `当前有 ${runningCount} 条 live run 正在推进,先从 tower 看全局。` + : `${runningCount} live runs are moving right now. Start in the tower before drilling into lists.` + : locale === "zh-CN" + ? "第一条 run 出现后,tower 会成为你的主驾驶舱。" + : "The tower becomes the main cockpit as soon as the first delegated run exists.", + }, + { + href: failedCount > 0 ? latestFailureGovernanceHref : "/workflows", + badge: locale === "zh-CN" ? "What is blocked" : "What is blocked", + title: + failedCount > 0 + ? locale === "zh-CN" + ? "风险与堵点" + : "Risk and blockers" + : locale === "zh-CN" + ? "Workflow posture" + : "Workflow posture", + desc: + failedCount > 0 + ? locale === "zh-CN" + ? `目前有 ${failedCount} 条失败或高风险 run,先处理堵点,再决定是否继续放行。` + : `${failedCount} failed or high-risk runs need triage before you promote anything else.` + : locale === "zh-CN" + ? "当前没有明显失败面,直接从 Workflow Cases 看 owner、queue 和 next step。" + : "No obvious failure lane is dominating. Open Workflow Cases to inspect owner, queue, and next action.", + }, + { + href: "/pm", + badge: locale === "zh-CN" ? "What next" : "What next", + title: + hasRunHistory + ? locale === "zh-CN" + ? "继续派发下一项任务" + : "Queue the next task" + : locale === "zh-CN" + ? "开始第一项任务" + : "Start the first task", + desc: + hasRunHistory + ? locale === "zh-CN" + ? "确认 tower 状态后,回到 PM 入口继续派发新任务。" + : "After you scan the tower, return to PM intake to dispatch the next piece of work." + : locale === "zh-CN" + ? "先把第一条任务送进系统,再回来用 tower 观察它。" + : "Start from PM intake, then come back here to watch the first task move through the tower.", + }, + ]; + const operatorLoopCards = [ + { + href: "/pm", + badge: "1", + title: "Plan", + desc: + locale === "zh-CN" + ? "从 PM 入口写清目标、约束和验收口径。" + : "Define the objective, constraints, and acceptance bar from PM intake.", + }, + { + href: "/pm", + badge: "2", + title: "Delegate", + desc: + locale === "zh-CN" + ? "把计划变成真实队列任务,不要停在说明文档里。" + : "Turn the plan into queued work instead of leaving it as a note.", + }, + { + href: "/command-tower", + badge: "3", + title: "Track", + desc: + locale === "zh-CN" + ? "用 live tower 先看现在到底在发生什么。" + : "Use the live tower to see what is happening right now.", + }, + { + href: "/workflows", + badge: "4", + title: "Resume", + desc: + locale === "zh-CN" + ? "回到 Workflow Case,沿 durable state 继续推进。" + : "Use Workflow Cases as the durable operating record.", + }, + { + href: "/runs", + badge: "5", + title: "Prove", + desc: + locale === "zh-CN" + ? "用 run 结果、失败线索和 replay 证据收口。" + : "Use run results, failure clues, and replayable evidence to close the loop.", + }, + ]; - const primaryActionLabel = hasRunHistory - ? homePhase2Copy.startNewTaskLabel - : homePhase2Copy.startFirstTaskLabel; + const primaryActionHref = hasRunHistory ? "/command-tower" : "/pm"; const topSecondaryAction = failedCount > 0 ? { href: failureRate >= 0.5 ? "/events" : latestFailureGovernanceHref, @@ -51,8 +206,8 @@ export default function DashboardHomeStorySections({ } : hasRunHistory ? { - href: "/runs", - label: homePhase2Copy.viewLatestRunsLabel, + href: "/pm", + label: shellCopy.secondaryAction, variant: "secondary" as ButtonVariant, } : null; @@ -62,55 +217,43 @@ export default function DashboardHomeStorySections({
+

{shellCopy.eyebrow}

- {homePhase2Copy.heroTitle} + {shellCopy.heroTitle}

-

{homePhase2Copy.heroSubtitle}

+

{shellCopy.heroSubtitle}

+

+ {locale === "zh-CN" + ? "首页第一屏先回答四件事:你现在在哪、系统正在发生什么、哪里堵住了、下一步该进哪条操作路径。" + : "The first screen should answer four questions immediately: where you are, what is happening now, what is blocked, and which surface to open next."} +

-
+
-

- {homePhase2Copy.productSpineTitle} +

+ {shellCopy.deskTitle}

-

{homePhase2Copy.productSpineDescription}

+

{shellCopy.deskDescription}

- {homePhase2Copy.productSpineCards.map((item) => ( - - {item.title} - {item.desc} - - ))} -
-
- -
-
-
-

- {homePhase2Copy.publicAdvantagesTitle} -

-

{homePhase2Copy.publicAdvantagesDescription}

-
-
-
- {homePhase2Copy.publicAdvantageCards.map((item) => ( - + {controlDeskCards.map((item) => ( + + {item.badge} {item.title} {item.desc} @@ -118,73 +261,117 @@ export default function DashboardHomeStorySections({
-
+
-

- {homePhase2Copy.publicTemplatesTitle} +

+ {shellCopy.loopTitle}

-

{homePhase2Copy.publicTemplatesDescription}

+

{shellCopy.loopDescription}

-
- {homePhase2Copy.publicTemplateCards.map((item) => ( - + {operatorLoopCards.map((item) => ( + {item.badge} {item.title} {item.desc} - Best for: {item.bestFor} - Try with: {item.example} - {item.proof} - {item.fields.join(" · ")} ))}
-
+
-

- {homePhase2Copy.integrationTitle} +

+ {shellCopy.guidesTitle}

-

{homePhase2Copy.integrationDescription}

+

{shellCopy.guidesDescription}

- -
-
- {adoptionCards.map((item) => { - const href = resolveHomeHref(item.href); - return ( - - {item.badge} - {item.title} - {item.desc} - - ); - })}
-

- Need the broader ecosystem framing? {homePhase2Copy.ecosystemAction}. - Need the full package ladder in one place?{" "} - - {homePhase2Copy.builderQuickstartCtaLabel} - . -

+ +
+ {shellCopy.guidesSummary} +
+
+
+

{shellCopy.methodTitle}

+

{shellCopy.methodDescription}

+
+
+
+ {homePhase2Copy.publicAdvantageCards.map((item) => ( + + {item.title} + {item.desc} + + ))} +
+ +
+
+

{shellCopy.templatesTitle}

+

{shellCopy.templatesDescription}

+
+ +
+
+ {homePhase2Copy.publicTemplateCards.map((item) => ( + + {item.badge} + {item.title} + {item.desc} + Best for: {item.bestFor} + Try with: {item.example} + {item.proof} + {item.fields.join(" · ")} + + ))} +
+ +
+
+

{shellCopy.adoptionTitle}

+

{shellCopy.adoptionDescription}

+
+ +
+
+ {adoptionCards.map((item) => { + const href = resolveHomeHref(item.href); + return ( + + {item.badge} + {item.title} + {item.desc} + + ); + })} +
+

+ Need the broader ecosystem framing? {homePhase2Copy.ecosystemAction}. + Need the full package ladder in one place?{" "} + + {homePhase2Copy.builderQuickstartCtaLabel} + . +

+
+
+
{showFirstTaskGuide ? ( diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 64d3790a..8a94bf16 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -11,7 +11,7 @@ "test:ct-regression": "DASHBOARD_VITEST_POOL=${DASHBOARD_VITEST_POOL:-forks} vitest run --config vitest.config.mts --coverage --coverage.thresholds.lines=0 --coverage.thresholds.statements=0 --coverage.thresholds.functions=0 --coverage.thresholds.branches=0 tests/command_tower_ui.test.tsx tests/command_tower_async_home.suite.tsx tests/command_tower_async_session.suite.tsx", "ui:audit": "bash ../../scripts/ui_audit_gate.sh", "lint": "npm run typecheck", - "typecheck": "tsc --noEmit -p tsconfig.json" + "typecheck": "tsc --noEmit -p tsconfig.typecheck.json" }, "dependencies": { "@cortexpilot/frontend-api-client": "file:../../packages/frontend-api-client", diff --git a/apps/dashboard/tests/agents_page_pagination_accessibility.suite.tsx b/apps/dashboard/tests/agents_page_pagination_accessibility.suite.tsx index 61dc7d94..c22424c4 100644 --- a/apps/dashboard/tests/agents_page_pagination_accessibility.suite.tsx +++ b/apps/dashboard/tests/agents_page_pagination_accessibility.suite.tsx @@ -102,7 +102,7 @@ describe("agents page pagination semantics", () => { expect(screen.getByText("Failure-led queue")).toBeInTheDocument(); expect(screen.getByText("Registered capacity")).toBeInTheDocument(); expect(screen.getByText("Pending scheduling backlog")).toBeInTheDocument(); - expect(screen.getByRole("heading", { name: "Role catalog (read-only first screen)" })).toBeInTheDocument(); + expect(screen.getByText("Role desk (read-only mirror)")).toBeInTheDocument(); expect(screen.queryByRole("link", { name: "Previous" })).toBeNull(); expect(screen.queryByRole("link", { name: "Next" })).toBeNull(); diff --git a/apps/dashboard/tests/command_tower_page_ssr_query_repro.test.ts b/apps/dashboard/tests/command_tower_page_ssr_query_repro.test.ts index 60ff7d0a..2cc51abc 100644 --- a/apps/dashboard/tests/command_tower_page_ssr_query_repro.test.ts +++ b/apps/dashboard/tests/command_tower_page_ssr_query_repro.test.ts @@ -19,7 +19,7 @@ describe("command tower page SSR query reproduction", () => { }); it("publishes route-level metadata for discoverability", () => { - expect(metadata.title).toBe("Command Tower | CortexPilot"); + expect(metadata.title).toBe("Command Tower | OpenVibeCoding"); expect(String(metadata.description)).toContain("operator visibility"); }); }); diff --git a/apps/dashboard/tests/command_tower_ui.test.tsx b/apps/dashboard/tests/command_tower_ui.test.tsx index c19a0abf..b104c96b 100644 --- a/apps/dashboard/tests/command_tower_ui.test.tsx +++ b/apps/dashboard/tests/command_tower_ui.test.tsx @@ -251,7 +251,7 @@ describe("command tower ui surfaces", () => { }); it("exports command tower metadata for route-level discoverability", () => { - expect(commandTowerMetadata.title).toBe("Command Tower | CortexPilot"); + expect(commandTowerMetadata.title).toBe("Command Tower | OpenVibeCoding"); expect(commandTowerMetadata.description).toContain("Workflow Cases"); }); diff --git a/apps/dashboard/tests/home_page.test.tsx b/apps/dashboard/tests/home_page.test.tsx index 3e0268ae..d6f2db90 100644 --- a/apps/dashboard/tests/home_page.test.tsx +++ b/apps/dashboard/tests/home_page.test.tsx @@ -57,12 +57,16 @@ describe("dashboard home run-summary clarity", () => { it("renders first-run CTA and onboarding guidance when no runs", async () => { render(await Home()); + const secondLayerGuides = screen.getByTestId("home-second-layer-guides"); + secondLayerGuides.setAttribute("open", ""); - expect(screen.getByRole("heading", { name: "The command tower for AI engineering" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "The open command tower for AI engineering" })).toBeInTheDocument(); expect( screen.getByText(/Stop babysitting AI coding work\./) ).toBeInTheDocument(); - expect(screen.getByText("Three engineering layers behind the command tower")).toBeInTheDocument(); + expect(screen.getByText("Second-layer guides, not the first impression")).toBeInTheDocument(); + expect(screen.getByText("Open second-layer guides")).toBeInTheDocument(); + expect(screen.getByText("Method layer, not the hero")).toBeInTheDocument(); expect(screen.getByText("Prompt Engineering")).toBeInTheDocument(); expect(screen.getByText("Context Engineering")).toBeInTheDocument(); expect(screen.getByText("Harness Engineering")).toBeInTheDocument(); @@ -86,7 +90,7 @@ describe("dashboard home run-summary clarity", () => { "href", "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" ); - expect(screen.getByText("Choose the right adoption path")).toBeInTheDocument(); + expect(screen.getByText("Extended surfaces")).toBeInTheDocument(); expect(screen.getByRole("link", { name: "See first proven workflow" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" @@ -125,7 +129,7 @@ describe("dashboard home run-summary clarity", () => { "https://xiaojiou176-open.github.io/CortexPilot-public/api/" ); expect(screen.getByText("Live Workflow Case gallery")).toBeInTheDocument(); - expect(screen.getByRole("link", { name: "Open Workflow Cases" })).toHaveAttribute("href", "/workflows"); + expect(screen.getAllByRole("link", { name: "Open Workflow Cases" })[0]).toHaveAttribute("href", "/workflows"); expect(screen.getByRole("link", { name: "See first proven workflow" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/use-cases/" @@ -135,7 +139,7 @@ describe("dashboard home run-summary clarity", () => { expect(screen.getByText("Stable: no recent failed runs (0%)")).toHaveClass("badge--success"); expect(screen.getByRole("progressbar", { name: "Failure share 0/0" })).toBeInTheDocument(); expect(screen.getByRole("link", { name: "View all runs" })).toHaveAttribute("href", "/runs"); - expect(screen.queryByRole("link", { name: "Open Command Tower" })).not.toBeInTheDocument(); + expect(screen.getByText("Live command tower").closest("a")).toHaveAttribute("href", "/command-tower"); expect(screen.queryByRole("link", { name: "Quick approval" })).not.toBeInTheDocument(); }, 30000); @@ -225,10 +229,12 @@ describe("dashboard home run-summary clarity", () => { }); render(await Home()); + const secondLayerGuides = screen.getByTestId("home-second-layer-guides"); + secondLayerGuides.setAttribute("open", ""); - expect(screen.getByRole("heading", { name: "AI 工程的指挥塔" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "面向 AI 工程的开放指挥塔" })).toBeInTheDocument(); expect(screen.getByRole("link", { name: "启动首个任务" })).toHaveAttribute("href", "/pm"); - expect(screen.getByText("选择正确的采用路径")).toBeInTheDocument(); + expect(screen.getByText("延伸入口")).toBeInTheDocument(); expect(screen.getByRole("link", { name: "打开 AI + MCP + API 页面" })).toHaveAttribute( "href", "https://xiaojiou176-open.github.io/CortexPilot-public/ai-surfaces/" @@ -416,7 +422,7 @@ describe("dashboard home run-summary clarity", () => { }); it("renders the public shell with English-first layout metadata and chrome copy", () => { - expect(metadata.title).toBe("CortexPilot | The command tower for AI engineering"); + expect(metadata.title).toBe("OpenVibeCoding | The open command tower for AI engineering"); expect(metadata.description).toContain("Stop babysitting AI coding work."); render( @@ -427,8 +433,9 @@ describe("dashboard home run-summary clarity", () => { expect(screen.getByRole("link", { name: "Skip to dashboard content" })).toHaveAttribute("href", "#dashboard-content"); expect(screen.getAllByLabelText("Dashboard navigation").length).toBeGreaterThan(0); - expect(screen.getByText("Plan · Delegate · Track · Resume · Prove")).toBeInTheDocument(); - expect(screen.getByText("AI engineering command tower")).toBeInTheDocument(); + expect(screen.getByText("OpenVibeCoding")).toBeInTheDocument(); + expect(screen.getByText("plan / delegate / track / resume / prove")).toBeInTheDocument(); + expect(screen.getByText("OpenVibeCoding command tower")).toBeInTheDocument(); expect(screen.getByLabelText("Platform status overview")).toBeInTheDocument(); expect(screen.getByText("Governance view")).toBeInTheDocument(); expect(screen.getByText("Live verification required")).toBeInTheDocument(); diff --git a/apps/dashboard/tests/planner_page.test.tsx b/apps/dashboard/tests/planner_page.test.tsx new file mode 100644 index 00000000..6c9b211c --- /dev/null +++ b/apps/dashboard/tests/planner_page.test.tsx @@ -0,0 +1,112 @@ +import { render, screen } from "@testing-library/react"; +import type { ReactNode } from "react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { mockCookies } = vi.hoisted(() => ({ + mockCookies: vi.fn(), +})); + +vi.mock("next/link", () => ({ + default: ({ href, children, ...props }: { href: string; children: ReactNode }) => ( + + {children} + + ), +})); + +vi.mock("next/headers", () => ({ + cookies: mockCookies, +})); + +vi.mock("next/navigation", () => ({ + useRouter: () => ({ + refresh: vi.fn(), + }), +})); + +vi.mock("../lib/api", () => ({ + fetchRuns: vi.fn(), + fetchRun: vi.fn(), + fetchArtifact: vi.fn(), + fetchReports: vi.fn(), + mutationExecutionCapability: vi.fn(() => ({ executable: false, operatorRole: null })), + enqueueRunQueue: vi.fn(), + runNextQueue: vi.fn(), +})); + +import PlannerPage from "../app/planner/page"; +import { fetchArtifact, fetchReports, fetchRun, fetchRuns } from "../lib/api"; + +describe("planner page", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockCookies.mockResolvedValue({ + get: () => undefined, + toString: () => "", + }); + vi.mocked(fetchRuns).mockResolvedValue([ + { run_id: "run-plan-1", task_id: "task-plan-1", status: "SUCCESS" }, + ] as never); + vi.mocked(fetchRun).mockResolvedValue({ + run_id: "run-plan-1", + task_id: "task-plan-1", + status: "SUCCESS", + manifest: { + artifacts: [ + { name: "planning_wave_plan", path: "artifacts/planning_wave_plan.json" }, + { name: "planning_worker_prompt_contracts", path: "artifacts/planning_worker_prompt_contracts.json" }, + { name: "planning_unblock_tasks", path: "artifacts/planning_unblock_tasks.json" }, + ], + }, + } as never); + vi.mocked(fetchArtifact).mockImplementation(async (_runId: string, name: string) => { + if (name === "planning_wave_plan.json") { + return { + name, + data: { + objective: "Ship one planning bridge", + worker_count: 3, + wake_policy_ref: "policies/control_plane_runtime_policy.json#/wake_policy", + }, + } as never; + } + if (name === "planning_worker_prompt_contracts.json") { + return { + name, + data: [{ prompt_contract_id: "worker-1" }, { prompt_contract_id: "worker-2" }], + } as never; + } + return { + name, + data: [{ unblock_task_id: "unblock-1" }], + } as never; + }); + vi.mocked(fetchReports).mockResolvedValue([ + { + name: "completion_governance_report.json", + data: { + overall_verdict: "continue_same_session", + continuation_decision: { selected_action: "reply_auditor_reprompt_and_continue_same_session" }, + }, + }, + ] as never); + }); + + it("renders a triage-first planner desk from planning artifacts", async () => { + render(await PlannerPage()); + + expect(screen.getByRole("heading", { name: "Planner desk" })).toBeInTheDocument(); + expect(screen.getByRole("heading", { level: 2, name: "Planner triage queue" })).toBeInTheDocument(); + expect(screen.getAllByText("Ship one planning bridge")).toHaveLength(2); + expect(screen.getAllByText(/Wake policy:/)).toHaveLength(2); + expect(screen.getByText("Missing worker prompt contract")).toBeInTheDocument(); + expect(screen.getByText("Planned workers: 3 · Worker contracts: 2 · Unblock tasks: 1")).toBeInTheDocument(); + expect(screen.getAllByRole("link", { name: "Open PM intake" })[0]).toHaveAttribute("href", "/pm"); + expect(screen.getAllByRole("link", { name: "Open Command Tower" })[0]).toHaveAttribute("href", "/command-tower"); + expect(screen.getAllByRole("link", { name: "Open Workflow Cases" })[0]).toHaveAttribute("href", "/workflows"); + expect(screen.getByRole("link", { name: "run-plan-1" })).toHaveAttribute("href", "/runs/run-plan-1"); + expect(screen.getAllByRole("button", { name: "Queue latest run contract" })).toHaveLength(2); + expect(screen.getAllByRole("button", { name: "Run next queued task" })).toHaveLength(2); + expect(screen.getByText("Planning inspection archive")).toBeInTheDocument(); + }); +}); diff --git a/apps/dashboard/tests/run_compare_page.test.tsx b/apps/dashboard/tests/run_compare_page.test.tsx index 21baf316..8de3a7a3 100644 --- a/apps/dashboard/tests/run_compare_page.test.tsx +++ b/apps/dashboard/tests/run_compare_page.test.tsx @@ -87,5 +87,6 @@ describe("run compare decision surface", () => { expect(screen.getByText("AI compare copilot")).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Explain these deltas" })).toBeInTheDocument(); expect(screen.getByRole("link", { name: "Open run detail" })).toHaveAttribute("href", "/runs/run-compare-1"); + expect(screen.getByText("Evidence archive")).toBeInTheDocument(); }); }); diff --git a/apps/dashboard/tests/run_detail_page.test.tsx b/apps/dashboard/tests/run_detail_page.test.tsx index cc7a909d..fbb9bf04 100644 --- a/apps/dashboard/tests/run_detail_page.test.tsx +++ b/apps/dashboard/tests/run_detail_page.test.tsx @@ -40,8 +40,10 @@ describe("run detail page copy", () => { it("renders the English-first page title and summary copy", async () => { render(await RunDetailPage({ params: Promise.resolve({ id: "run-1" }) })); - expect(screen.getByTestId("run-detail-title")).toHaveTextContent("Run detail"); - expect(screen.getByText("Follow one run across status, event evidence, and replay comparison.")).toBeInTheDocument(); + expect(screen.getByTestId("run-detail-title")).toHaveTextContent("Proof room"); + expect( + screen.getByText("Follow one run across live status, event evidence, and replay comparison before you trust the result."), + ).toBeInTheDocument(); expect(screen.getByText("AI operator copilot")).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Generate operator brief" })).toBeInTheDocument(); expect(screen.getByTestId("run-detail-stub")).toHaveTextContent("run-1"); @@ -55,8 +57,8 @@ describe("run detail page copy", () => { render(await RunDetailPage({ params: Promise.resolve({ id: "run-zh" }) })); - expect(screen.getByTestId("run-detail-title")).toHaveTextContent("运行详情"); - expect(screen.getByText("沿着状态、事件证据和回放对比,完整跟踪这一条 Run。")).toBeInTheDocument(); + expect(screen.getByTestId("run-detail-title")).toHaveTextContent("证明室"); + expect(screen.getByText("沿着实时状态、事件证据和回放对比,完整跟踪这一条 Run,再决定是否信任结果。")).toBeInTheDocument(); expect(screen.getByRole("link", { name: "打开对比视图" })).toHaveAttribute("href", "/runs/run-zh/compare"); }); diff --git a/apps/dashboard/tests/runlist.test.tsx b/apps/dashboard/tests/runlist.test.tsx index 30ec5db3..04b404c2 100644 --- a/apps/dashboard/tests/runlist.test.tsx +++ b/apps/dashboard/tests/runlist.test.tsx @@ -257,10 +257,10 @@ test("keeps contrast-safe muted and badge text tokens in dashboard globals", () })(); const css = readCssBundle(cssPath); - expect(css).toContain("--color-text-muted: #4b5563;"); - expect(css).toContain("--color-success-ink: #065f46;"); - expect(css).toContain("--color-warning-ink: #92400e;"); - expect(css).toContain("--color-danger-ink: #b91c1c;"); + expect(css).toContain("--color-text-muted: #9fb0c8;"); + expect(css).toContain("--color-success-ink: #7fe0b7;"); + expect(css).toContain("--color-warning-ink: #f2c27d;"); + expect(css).toContain("--color-danger-ink: #f0aaaa;"); expect(css).toContain(".quick-card-desc"); expect(css).toContain(".ct-home-filter-desc"); expect(css).toContain(".sidebar-link"); diff --git a/apps/dashboard/tests/workflow_detail_page.test.tsx b/apps/dashboard/tests/workflow_detail_page.test.tsx index f2bd5770..0d7e0d5b 100644 --- a/apps/dashboard/tests/workflow_detail_page.test.tsx +++ b/apps/dashboard/tests/workflow_detail_page.test.tsx @@ -138,7 +138,7 @@ describe("workflow detail page", () => { await expect( generateMetadata({ params: Promise.resolve({ id: "wf-1" }) }), ).resolves.toMatchObject({ - title: "Workflow Case detail · wf-1 | CortexPilot", + title: "Workflow Case detail · wf-1 | OpenVibeCoding", }); }); @@ -231,7 +231,7 @@ describe("workflow detail page", () => { }); render(view); - expect(screen.getByText("Runs: 0")).toBeInTheDocument(); + expect(screen.getByText("Proof & Replay: 0")).toBeInTheDocument(); expect(screen.getAllByText("High-risk state").length).toBeGreaterThan(0); expect(screen.getByText("No related runs")).toBeInTheDocument(); expect(screen.queryByRole("link", { name: "bad-shape" })).not.toBeInTheDocument(); diff --git a/apps/dashboard/tests/workflows_queue_page.test.tsx b/apps/dashboard/tests/workflows_queue_page.test.tsx index d2272e00..f3eaa01f 100644 --- a/apps/dashboard/tests/workflows_queue_page.test.tsx +++ b/apps/dashboard/tests/workflows_queue_page.test.tsx @@ -79,7 +79,7 @@ describe("workflows queue page", () => { }); it("exports workflow list metadata for route-level discoverability", () => { - expect(workflowsMetadata.title).toBe("Workflow Cases | CortexPilot"); + expect(workflowsMetadata.title).toBe("Workflow Cases | OpenVibeCoding"); expect(workflowsMetadata.description).toContain("queue posture"); }); diff --git a/apps/desktop/index.html b/apps/desktop/index.html index 8c461d46..e8ade15c 100644 --- a/apps/desktop/index.html +++ b/apps/desktop/index.html @@ -3,7 +3,7 @@ - CortexPilot Command Tower Desktop + OpenVibeCoding Command Tower Desktop
diff --git a/apps/desktop/src/App.test.tsx b/apps/desktop/src/App.test.tsx index 19ec7dcf..2181800c 100644 --- a/apps/desktop/src/App.test.tsx +++ b/apps/desktop/src/App.test.tsx @@ -727,7 +727,7 @@ describe("Desktop command center shell", { timeout: 15000 }, () => { const pageNavigation = await screen.findByRole("navigation", { name: /页面组导航|Page group navigation/ }); await user.click(within(pageNavigation).getByRole("button", { name: /指挥塔|Command Tower/ })); await screen.findByText( - /Desktop 聚焦执行动作与异常裁决;治理分析默认转到 Web 深度视图。|Desktop stays focused on execution and operator decisions/, + /OpenVibeCoding 的桌面端聚焦执行与操作决策;更深的治理分析仍留给 Web 视图。|OpenVibeCoding on desktop stays focused on execution and operator decisions/, ); expect(await screen.findByRole("button", { name: /暂停自动更新|Pause auto-refresh/ })).toBeInTheDocument(); @@ -837,7 +837,7 @@ describe("Desktop command center shell", { timeout: 15000 }, () => { render(); await user.click(screen.getByRole("button", { name: /指挥塔|Command Tower/ })); - await screen.findByText(/Desktop 聚焦执行动作与异常裁决;治理分析默认转到 Web 深度视图。|Desktop stays focused on execution and operator decisions/); + await screen.findByText(/OpenVibeCoding 的桌面端聚焦执行与操作决策;更深的治理分析仍留给 Web 视图。|OpenVibeCoding on desktop stays focused on execution and operator decisions/); expect(screen.getByRole("button", { name: /更新进展|Refresh progress/ })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /暂停自动更新|Pause auto-refresh/ })).toBeInTheDocument(); @@ -847,22 +847,23 @@ describe("Desktop command center shell", { timeout: 15000 }, () => { it("covers sidebar governance routes and keeps topbar title in sync", async () => { const user = userEvent.setup(); render(); + const pageNavigation = await screen.findByRole("navigation", { name: /页面组导航|Page group navigation/ }); const routeCases = [ - { nav: /运行记录|Runs/, title: "Runs" }, + { nav: /运行记录|Proof & Replay|Runs/, title: "Proof & Replay" }, { nav: /工作流|Workflow Cases|Workflows/, title: "Workflow Cases" }, { nav: /事件流|Events/, title: "Events" }, - { nav: /合约|Contracts/, title: "Contracts" }, + { nav: /合约桌|Contracts|Contract desk/, title: "Contract desk" }, { nav: /评审|Reviews/, title: "Reviews" }, { nav: /测试|Tests/, title: "Tests" }, { nav: /策略|Policies/, title: "Policies" }, - { nav: /代理|Agents/, title: "Agents" }, + { nav: /角色桌|Agents|Role desk/, title: "Role desk" }, { nav: /锁管理|Locks/, title: "Locks" }, { nav: /工作树|Worktrees/, title: "Worktrees" }, ] as const; for (const routeCase of routeCases) { - await user.click(screen.getByRole("button", { name: routeCase.nav })); + await user.click(within(pageNavigation).getByRole("button", { name: routeCase.nav })); await waitFor(() => { expectTopbarTitle(routeCase.title); }); @@ -926,11 +927,12 @@ describe("Desktop command center shell", { timeout: 15000 }, () => { const user = userEvent.setup(); render(); + const pageNavigation = await screen.findByRole("navigation", { name: /页面组导航|Page group navigation/ }); - await user.click(screen.getByRole("button", { name: /运行记录|Runs/ })); + await user.click(within(pageNavigation).getByRole("button", { name: /运行记录|Proof & Replay|Runs/ })); await user.click(await screen.findByRole("button", { name: "run-detail-t" })); await waitFor(() => { - expectTopbarTitle("Run Detail"); + expectTopbarTitle("Proof room"); }); }); @@ -1440,7 +1442,7 @@ describe("Desktop command center shell", { timeout: 15000 }, () => { await expectActiveSession("pm-live-1"); fireEvent.keyDown(window, { key: "m", altKey: true }); - expect(await screen.findByRole("heading", { name: /新手起步|Operator overview/ })).toBeInTheDocument(); + expect(await screen.findByRole("heading", { name: /指挥面总览|Command deck overview/ })).toBeInTheDocument(); fireEvent.keyDown(window, { key: "2", altKey: true }); expect(await screen.findByLabelText(/继续对话|Continue the conversation/)).toBeInTheDocument(); diff --git a/apps/desktop/src/components/layout/AppSidebar.tsx b/apps/desktop/src/components/layout/AppSidebar.tsx index 87304cde..2418ce2d 100644 --- a/apps/desktop/src/components/layout/AppSidebar.tsx +++ b/apps/desktop/src/components/layout/AppSidebar.tsx @@ -121,15 +121,18 @@ type AppSidebarProps = { locale?: UiLocale; }; +const PUBLIC_BRAND_TITLE = "OpenVibeCoding"; +const PUBLIC_BRAND_SUBTITLE = "plan / delegate / track / resume / prove"; + export function AppSidebar({ activePage, onNavigate, locale = DEFAULT_UI_LOCALE }: AppSidebarProps) { const uiCopy = getUiCopy(locale); const navSections: NavSection[] = [ { title: uiCopy.desktop.sectionPrimary, items: [ + { page: "command-tower", label: uiCopy.desktop.labels.commandTower, icon: "tower" }, { page: "pm", label: uiCopy.desktop.labels.pmIntake, icon: "message" }, { page: "search", label: uiCopy.desktop.labels.search, icon: "search" }, - { page: "command-tower", label: uiCopy.desktop.labels.commandTower, icon: "tower" }, ], }, { @@ -164,11 +167,11 @@ export function AppSidebar({ activePage, onNavigate, locale = DEFAULT_UI_LOCALE type="button" unstyled className="brand-link sidebar-brand-reset" - onClick={() => onNavigate("pm")} + onClick={() => onNavigate("command-tower")} > - {uiCopy.brandTitle} + {PUBLIC_BRAND_TITLE} -

{uiCopy.brandSubtitle}

+

{PUBLIC_BRAND_SUBTITLE}