Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 62 additions & 2 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ jobs:
web: ${{ steps.filter.outputs.web }}
shell: ${{ steps.filter.outputs.shell }}
go: ${{ steps.filter.outputs.go }}
mobile: ${{ steps.filter.outputs.mobile }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
Expand Down Expand Up @@ -352,6 +353,13 @@ jobs:
- 'api/**'
- 'scripts/verify/**'
- '.github/workflows/checks.yml'
mobile:
- 'app/mobile-rn/**'
- 'app/shared/src/hubClient*.ts'
- 'app/shared/src/hubEvents*.ts'
- 'app/package.json'
- 'app/pnpm-lock.yaml'
- '.github/workflows/checks.yml'

# ── Frontend: Desktop ────────────────────────
# Known debt: 15 pre-existing test failures (97.8% pass rate: 679/694).
Expand Down Expand Up @@ -434,8 +442,39 @@ jobs:
- name: Test
run: pnpm test -- --run

# ── Frontend: Mobile ──────────────────────────
# 重 job(Playwright/Expo export);仅 workflow_dispatch 成本节流。
# ── Frontend: Mobile light (PR path-filtered) ──
# typecheck + unit tests only. No Expo export / Playwright / doctor.
# Closes the post-#1341 gap where mobile PRs skipped all mobile CI.
frontend-mobile-light:
name: Frontend (mobile light)
needs: changes
if: >-
github.event_name == 'workflow_dispatch' ||
needs.changes.outputs.mobile == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: ./app
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: app/pnpm-lock.yaml
- name: Install
run: pnpm install --frozen-lockfile
- name: Type check (mobile)
run: pnpm --filter agenthub-mobile-rn typecheck
- name: Unit tests (mobile)
run: pnpm --filter agenthub-mobile-rn test

# ── Frontend: Mobile full ───────────────────────
# 重 job(Playwright/Expo export/coverage);仅 workflow_dispatch 成本节流。
# job 体保留以满足 verify-ci-gates.ps1;产品 LIVE,非停运。
frontend-mobile:
name: Frontend (mobile)
Expand Down Expand Up @@ -603,6 +642,27 @@ jobs:
if-no-files-found: warn
retention-days: 7

# ── Backend perf / leak gates ─────────────────
# Behavior + short microbench only (not production capacity).
# Manual trigger only — residual T5 / post-polish risk assessment.
backend-perf-leak-gates:
name: Backend perf/leak gates
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: true
cache-dependency-path: |
hub-server/go.sum
edge-server/go.sum
- name: Run backend perf/leak gates
shell: pwsh
run: ./scripts/verify/verify-backend-perf-leak-gates.ps1 -Benchtime 100ms

# ── E2E Smoke ─────────────────────────────────
# 重 job;仅 workflow_dispatch 成本节流,需要时手动触发。
e2e-smoke:
Expand Down
5 changes: 5 additions & 0 deletions app/mobile-rn/src/theme/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ export const agentHubMobileTokenAliases = {
'--td-shadow-lg': 'shadow.lg',
'--td-shadow-panel': 'shadow.panel',
'--td-shadow-hairline': 'shadow.hairline',
// Desktop glass contract (DESKTOP_GLASS_TOKEN_ALIASES).
// RN has no CSS backdrop-filter; map blur/elev to nearest elevation proxies.
'--td-glass-blur': 'shadow.lg',
'--td-glass-card': 'color.surface',
'--td-glass-elev': 'shadow.md',
} as const;

const semanticTypeRoles = {
Expand Down
2 changes: 1 addition & 1 deletion docs/analysis/post-polish-risk-assessment.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
| T2 | RN Hub-only boundary note | **Done** #1341 (AGENTS + mobile README) |
| T3 | Docs hygiene plan/* + MASTER | **Done** #1340 / #1342 |
| T4 | Optional: Desktop demo seed fail-closed | **Open optional** (AH-SR-043 residual) |
| T5 | Optional: perf-gate workflow_dispatch | **Open optional** |
| T5 | Optional: perf-gate workflow_dispatch | **Done** — `backend-perf-leak-gates` job in checks.yml (dispatch-only) |

## 6. Explicit Out of Scope

Expand Down
1 change: 1 addition & 0 deletions docs/progress/MASTER.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ Optional future: wire script as `workflow_dispatch` only — not every PR (see r
| 2026-07-20 | SDD Phase 0–1: post-polish analysis trio committed |
| 2026-07-21 | Residual program delivered: Phase 79 #1340 + Phase 80 #1341; milestones 98–99 closed; hubClient ~342 LOC |
| 2026-07-21 | Closeout #1342 + SSOT sync #1343 · tip `1ac86aa5`; analysis inventory marked delivered |
| 2026-07-21 | CI: mobile light path-filter + backend perf/leak `workflow_dispatch` (T5) |
4 changes: 3 additions & 1 deletion docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ AgentHub 是 IM 形态的多 Agent 协作工作台。用户面对的是联系人
| 方向 | 目标 | 验收 |
|---|---|---|
| 真实 E2E 合同 | `.agents/skills/real-e2e-acceptance/SKILL.md` 是唯一证据等级矩阵 | `scripts/verify/verify-real-e2e-contract.ps1` |
| 远控拓扑前置合同 | P0 remote-control fixture 验证 `Web -> Hub -> Desktop/Edge -> Local Edge -> CLI/SDK adapter` 的离线拓扑形状,不声明真实登录或真实执行 | `scripts/verify/verify-p0-remote-control-fixture.ps1` |
| Chat flow 可靠性 | 发送不消失、消息线性排序、自动滚动、卡片合并、markdown/table 渲染 | Desktop/Web Playwright + Visual QA |
| Hub/Edge 安全边界 | TokenDance ID 只做身份,AgentHub 权限由 Hub 本地资源检查决定 | 后端 auth/permission tests + security risk register |
| 文档 SSOT 保持 | MASTER/roadmap 与 closed residual 对齐;不复活 cleanup Phase 61 叙事 | `scripts/verify/verify-doc-ssot.ps1` |
Expand All @@ -42,7 +43,8 @@ AgentHub 是 IM 形态的多 Agent 协作工作台。用户面对的是联系人
|---|---|---|
| Web/Mobile client test lanes | Web 保持 Hub-only;Mobile 只澄清 RN-safe shared contract gate,不做 native/UI 深入重构 | Web data-boundary checks + Mobile mock-Hub/Expo Doctor boundary notes |
| Desktop packaged boundary | 区分 Vite renderer、Tauri packaged、sidecar、icon、installer/signing | packaged-release gate 或明确 `real_tested=false` |
| Backend performance/leak | 行为/微基准门禁已绿;不声明生产容量 | [reference/backend-performance-gates.md](reference/backend-performance-gates.md) + `scripts/verify/verify-backend-perf-leak-gates.ps1` **PASS**(非 capacity) |
| Backend performance/leak | 行为/微基准门禁已绿;不声明生产容量;**手动** `workflow_dispatch` job `Backend perf/leak gates` | [reference/backend-performance-gates.md](reference/backend-performance-gates.md) + `scripts/verify/verify-backend-perf-leak-gates.ps1` **PASS**(非 capacity) |
| Mobile PR CI light | mobile-rn / shared hubClient 变更跑 typecheck+unit;重 suite 仍 dispatch | checks.yml `frontend-mobile-light` path-filter |
| API contract hygiene | REST 以 `api/openapi.yaml` 为准,WS 以 `api/events.md` 为准 | OpenAPI parse + endpoint tests |

### P2
Expand Down
12 changes: 6 additions & 6 deletions hub-server/internal/middleware/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ func TestDefaultCORSOrigins(t *testing.T) {
env string
want string
}{
{name: "production", env: "production", want: "https://hub.vectorcontrol.tech,https://tauri.localhost"},
{name: "prod alias", env: "prod", want: "https://hub.vectorcontrol.tech,https://tauri.localhost"},
{name: "release alias", env: "release", want: "https://hub.vectorcontrol.tech,https://tauri.localhost"},
{name: "development", env: "development", want: "https://hub.vectorcontrol.tech,http://localhost:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174,https://tauri.localhost"},
{name: "staging", env: "staging", want: "https://hub.vectorcontrol.tech,http://localhost:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174,https://tauri.localhost"},
{name: "empty env", env: "", want: "https://hub.vectorcontrol.tech,http://localhost:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174,https://tauri.localhost"},
{name: "production", env: "production", want: "https://hub.tokendancelab.com,https://hub.vectorcontrol.tech,https://tauri.localhost"},
{name: "prod alias", env: "prod", want: "https://hub.tokendancelab.com,https://hub.vectorcontrol.tech,https://tauri.localhost"},
{name: "release alias", env: "release", want: "https://hub.tokendancelab.com,https://hub.vectorcontrol.tech,https://tauri.localhost"},
{name: "development", env: "development", want: "https://hub.tokendancelab.com,https://hub.vectorcontrol.tech,http://localhost:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174,https://tauri.localhost"},
{name: "staging", env: "staging", want: "https://hub.tokendancelab.com,https://hub.vectorcontrol.tech,http://localhost:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174,https://tauri.localhost"},
{name: "empty env", env: "", want: "https://hub.tokendancelab.com,https://hub.vectorcontrol.tech,http://localhost:3000,http://localhost:5173,http://127.0.0.1:5173,http://localhost:5174,http://127.0.0.1:5174,https://tauri.localhost"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
22 changes: 22 additions & 0 deletions scripts/verify/verify-ci-gates.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ $backendFocused = Get-JobBlock $workflow "backend-focused-subset"
$desktop = Get-JobBlock $workflow "frontend-desktop"
$web = Get-JobBlock $workflow "frontend-web"
$mobile = Get-JobBlock $workflow "frontend-mobile"
$mobileLight = Get-JobBlock $workflow "frontend-mobile-light"
$e2e = Get-JobBlock $workflow "e2e-smoke"
$changes = Get-JobBlock $workflow "changes"
$visualShell = Get-JobBlock $workflow "visual-qa-shell"
$validate = Get-JobBlock $workflow "validate"
$backendPerf = Get-JobBlock $workflow "backend-perf-leak-gates"

Assert-Contains $edge "Coverage check \(informational\)" "go-edge overall coverage must stay informational"
Assert-Contains $edge "Coverage per-package minimums" "go-edge must keep per-package coverage minimums"
Expand Down Expand Up @@ -132,6 +134,7 @@ foreach ($job in @(
@{ Name = "frontend-desktop"; Body = $desktop; Lockfile = "app/pnpm-lock.yaml" },
@{ Name = "frontend-web"; Body = $web; Lockfile = "app/pnpm-lock.yaml" },
@{ Name = "frontend-mobile"; Body = $mobile; Lockfile = "app/pnpm-lock.yaml" },
@{ Name = "frontend-mobile-light"; Body = $mobileLight; Lockfile = "app/pnpm-lock.yaml" },
@{ Name = "e2e-smoke"; Body = $e2e; Lockfile = "app/pnpm-lock.yaml" },
@{ Name = "visual-qa-shell"; Body = $visualShell; Lockfile = "app/pnpm-lock.yaml" }
)) {
Expand All @@ -154,6 +157,23 @@ Assert-Contains $validate "check-secrets\.sh" "validate job must keep secret gua
Assert-Contains $mobile "(?m)^\s+timeout-minutes:\s+45\s*$" "frontend-mobile job must have a hard timeout"
Assert-Contains (Get-StepBlock $mobile "Screenshot visual QA (mobile)") "(?m)^\s+timeout-minutes:\s+12\s*$" "mobile visual QA must have a hard timeout"
Assert-Contains (Get-StepBlock $mobile "E2E (mock hub)") "(?m)^\s+timeout-minutes:\s+10\s*$" "mobile mock-hub E2E must have a hard timeout"
Assert-Contains $mobile "github.event_name == 'workflow_dispatch'" "frontend-mobile full suite must stay workflow_dispatch-only"

Assert-Contains $mobileLight "Frontend \(mobile light\)" "frontend-mobile-light must use a clear job name"
Assert-Contains $mobileLight "needs:\s+changes" "frontend-mobile-light must depend on unified changes job"
Assert-Contains $mobileLight "needs.changes.outputs.mobile" "frontend-mobile-light must path-filter on mobile"
Assert-Contains $mobileLight "(?m)^\s+timeout-minutes:\s+15\s*$" "frontend-mobile-light job must have a hard timeout"
Assert-Contains $mobileLight ([regex]::Escape("pnpm --filter agenthub-mobile-rn typecheck")) "frontend-mobile-light must typecheck mobile"
Assert-Contains $mobileLight ([regex]::Escape("pnpm --filter agenthub-mobile-rn test")) "frontend-mobile-light must run mobile unit tests"
Assert-NotContains $mobileLight "npx expo export" "frontend-mobile-light must not run Expo export"
Assert-NotContains $mobileLight "scripts/visual-qa\.mjs" "frontend-mobile-light must not run mobile visual QA"
Assert-NotContains $mobileLight "playwright install --with-deps" "frontend-mobile-light must not install Playwright"

Assert-Contains $backendPerf "Backend perf/leak gates" "backend-perf-leak-gates must use a clear job name"
Assert-Contains $backendPerf "github.event_name == 'workflow_dispatch'" "backend-perf-leak-gates must be workflow_dispatch-only"
Assert-Contains $backendPerf ([regex]::Escape("verify-backend-perf-leak-gates.ps1")) "backend-perf-leak-gates must run the perf/leak script"
Assert-Contains $backendPerf "(?m)^\s+timeout-minutes:\s+20\s*$" "backend-perf-leak-gates must have a hard timeout"
Assert-NotContains $backendPerf "load-test" "backend-perf-leak-gates must not claim load/capacity smoke"

Assert-Contains $changes "dorny/paths-filter@v3" "changes job must use dorny/paths-filter"
Assert-Contains $changes ([regex]::Escape("app/shared/src/workbench/**")) "changes job must watch workbench paths"
Expand All @@ -163,6 +183,8 @@ Assert-Contains $changes ([regex]::Escape("app/desktop/scripts/visual-qa*")) "ch
Assert-Contains $changes ([regex]::Escape(".github/workflows/checks.yml")) "changes job must watch checks.yml"
Assert-Contains $changes ([regex]::Escape("hub-server/**")) "changes job must watch hub-server paths"
Assert-Contains $changes ([regex]::Escape("edge-server/**")) "changes job must watch edge-server paths"
Assert-Contains $changes ([regex]::Escape("app/mobile-rn/**")) "changes job must watch mobile-rn paths"
Assert-Contains $changes "(?m)^\s+mobile:\s*$" "changes job must expose mobile output"

Assert-Contains $visualShell "Visual QA shell \(web, path-filtered\)" "visual-qa-shell must use a clear job name"
Assert-Contains $visualShell "needs:\s+changes" "visual-qa-shell must depend on unified changes job"
Expand Down
Loading