` shell with no rendered content (~0 chars body text). The SPA fails to mount when hitting these routes directly.
-- **Expected:** Should either render content or redirect to correct working routes:
- - `/clusters` → `/configuration/clusters`
- - `/registries` → `/configuration/registries`
- - `/monitoring` → `/monitoring/clusters`
- - `/launch` → `/artifact/registries`
-- **Severity:** HIGH — Users who bookmark or type these URLs see blank pages
-- **Screenshot:** `01-login` (representative of empty state)
-
-**Working routes for reference:**
-- `/configuration/clusters` ✅
-- `/configuration/registries` ✅
-- `/monitoring/clusters` ✅
-- `/artifact/registries` ✅
-- `/artifact/instances` ✅
-
----
-
-### Bug #2: Launch Button Does Nothing When Clicked (🔴 HIGH)
-
-- **Page:** Chart Browser (`/artifact/registries`)
-- **Action:**
- 1. Navigate to `/artifact/registries`
- 2. Registry `harbor-bwgdi` loads with 13 charts
- 3. Expand `charts/chromadb` folder
- 4. Tag `0.1.4` appears with "Launch" and "Copy" buttons
- 5. Click the "Launch" button
-- **Actual:** No visible reaction — no modal opens, no URL change, no console error. The button is not disabled (no `disabled` attribute, no `aria-disabled`), is visibly styled as active (`bg-blue-50 text-blue-700 border-blue-200 shadow-sm`), and Playwright confirms `is_enabled() == True`. The React onClick handler produces no observable effect.
-- **Expected:** Clicking "Launch" on a chart tag should open a deployment form/dialog with cluster selector, instance name, namespace, and values configuration fields.
-- **Severity:** HIGH — Core platform feature (deploying Helm charts) is completely blocked
-- **Screenshot:** `04-chart-expanded`
-
----
-
-### Bug #3: Ambiguous "Launch" Button Labels (🟡 MEDIUM)
-
-- **Page:** Chart Browser (`/artifact/registries`)
-- **Action:** Inspect button accessible names
-- **Actual:** Both the sidebar navigation item "Launch Instance" and the chart action button "Launch" appear on the same page. The chart action button has no distinguishing `aria-label` or accessible description. The "Copy" button next to it has a `title="Copy pull command"` attribute, but "Launch" does not.
-- **Expected:** The chart action should have a descriptive label like `aria-label="Launch chart chromadb version 0.1.4"` to differentiate from the nav item.
-- **Severity:** MEDIUM — Accessibility concern; minor confusion for sighted users with multiple "Launch" targets
-
----
-
-## Test Results by Area
-
-| Area | Status | Notes |
-|------|--------|-------|
-| Login | ✅ PASS | test-user-a login successful, redirect to `/home` |
-| Home Page | ✅ PASS | All cards visible, nav clicks work, no Users section |
-| Sidebar Nav | ✅ PASS | All 6 items navigate correctly, Users hidden |
-| Chart Browser | ❌ BLOCKED | Registry loads, charts expand, but **Launch button dead** |
-| Instances | ✅ PASS | Empty state, filter, refresh all work |
-| Monitoring | ✅ PASS | 2 clusters, health data, CPU/Memory/GPU stats all load |
-| Config - Clusters | ✅ PASS | Both clusters listed, Add form opens |
-| Config - Registries | ✅ PASS | Harbor registry listed, Add form opens |
-| Direct Routes | ❌ FAIL | 4 routes return empty pages |
-
-## Screenshots
-
-- `01-login` → `/tmp/ocdp-qa-screenshots/01-login.png`
-- `02-home` → `/tmp/ocdp-qa-screenshots/02-home.png`
-- `02-home-full` → `/tmp/ocdp-qa-screenshots/02-home-full.png`
-- `04-chart-browser` → `/tmp/ocdp-qa-screenshots/04-chart-browser.png`
-- `04-chart-expanded` → `/tmp/ocdp-qa-screenshots/04-chart-expanded.png`
-- `04-launch-modal` → `/tmp/ocdp-qa-screenshots/04-launch-modal.png`
-- `05-instances` → `/tmp/ocdp-qa-screenshots/05-instances.png`
-- `06-monitoring` → `/tmp/ocdp-qa-screenshots/06-monitoring.png`
-- `07-clusters` → `/tmp/ocdp-qa-screenshots/07-clusters.png`
-- `07-add-cluster-form` → `/tmp/ocdp-qa-screenshots/07-add-cluster-form.png`
-- `08-registries` → `/tmp/ocdp-qa-screenshots/08-registries.png`
-- `08-add-registry-form` → `/tmp/ocdp-qa-screenshots/08-add-registry-form.png`
diff --git a/docs/bugs-user-b.md b/docs/bugs-user-b.md
deleted file mode 100644
index 093ed1c..0000000
--- a/docs/bugs-user-b.md
+++ /dev/null
@@ -1,149 +0,0 @@
-# Bug Report: test-user-b QA Test
-
-**Tester:** test-user-b (user role)
-**Date:** 2026-05-11
-**Environment:** http://10.6.80.114:18080
-
----
-
-## Bug 1: Repository Tags Endpoint Returns 404
-
-**Endpoint:** `GET /api/v1/registries/{registryId}/repositories/{repository}/tags`
-**Status Code:** 404
-**Response Body:** `404 page not found` (plain text, not JSON)
-
-**Expected:** Should return a list of tags for the chart/artifact.
-**Actual:** The dedicated tags endpoint is not implemented or routes incorrectly. The artifacts endpoint (`/repositories/{repository}/artifacts`) does work and returns tag info.
-
-**Severity:** Medium — tags are still discoverable via artifacts endpoint but the dedicated tags API is broken.
-
----
-
-## Bug 2: DELETE Instance Returns Empty Response Body
-
-**Endpoint:** `DELETE /api/v1/clusters/{clusterId}/instances/{instanceId}`
-**Status Code:** 200
-**Response Body:** (empty — no content at all)
-
-**Expected:** Should return a confirmation JSON body (e.g., `{"message": "Instance deletion initiated", "id": "..."}`) or at minimum a 202 Accepted with status details.
-
-**Actual:** Returns a completely empty body. The instance does transition to `pending-delete` state, but the API consumer receives no feedback.
-
-**Severity:** Medium — operation works but API consumer gets no confirmation.
-
----
-
-## Bug 3: Cluster Stats Endpoint Returns 404
-
-**Endpoint:** `GET /api/v1/clusters/{clusterId}/stats`
-**Status Code:** 404
-**Response Body:** `404 page not found` (plain text)
-
-**Expected:** Should return cluster resource statistics (CPU, memory, pod counts, etc.) or a proper JSON error if not implemented.
-
-**Actual:** Endpoint is not implemented — returns a raw 404 with no JSON error structure.
-
-**Severity:** Low — but given the user has `monitoring:clusters:view` permission, this is a missing feature.
-
----
-
-## Bug 4: Kubeconfig Endpoint Returns 404
-
-**Endpoint:** `GET /api/v1/clusters/{clusterId}/kubeconfig`
-**Status Code:** 404
-**Response Body:** `404 page not found` (plain text)
-
-**Expected:** Should return a kubeconfig file content or JSON error. User has `kubeconfig:issue:own` permission.
-
-**Actual:** Endpoint is not implemented.
-
-**Severity:** Low — the permission exists but the endpoint does nothing.
-
----
-
-## Bug 5: Monitoring Metrics Endpoint Returns 404
-
-**Endpoint:** `GET /api/v1/monitoring/clusters/{clusterId}/metrics`
-**Status Code:** 404
-**Response Body:** `404 page not found` (plain text)
-
-**Expected:** Monitoring metrics data. User has `monitoring:clusters:view` permission.
-
-**Actual:** Endpoint not found.
-
-**Severity:** Low — monitoring permissions exist but backend endpoints missing.
-
----
-
-## Bug 6: Inconsistent API Response Format (Array vs Object Wrapper)
-
-**Clusters and Registries** return bare arrays:
-```json
-[
- { "id": "...", "name": "k3s", ... }
-]
-```
-
-**Instances** returns an object wrapper:
-```json
-{
- "instances": [
- { "id": "...", "name": "test-nginx-b", ... }
- ],
- "total": 1
-}
-```
-
-**Expected:** Consistent response format across all list endpoints. Either all return bare arrays or all use the `{ "items": [...], "total": N }` wrapper pattern.
-
-**Severity:** Low — API consistency issue. Makes client code harder to write generically.
-
----
-
-## Bug 7: auth/me Returns Empty Token Fields
-
-**Endpoint:** `GET /api/v1/auth/me`
-**Response includes empty/unpopulated fields:**
-```json
-{
- "accessToken": "",
- "refreshToken": "",
- ...
-}
-```
-
-**Expected:** Either remove these fields from the `/auth/me` response (they are only meaningful in login/refresh responses) or populate them with valid values.
-
-**Actual:** Emptry string values for both token fields create confusion about whether they should be present.
-
-**Severity:** Low — cosmetic issue, but suggests the DTO is reusing the login response struct without clearing token fields.
-
----
-
-## Summary
-
-| # | Bug | Severity | Category |
-|---|-----|----------|----------|
-| 1 | Tags endpoint 404 | Medium | Missing Implementation |
-| 2 | DELETE returns empty body | Medium | API Response Quality |
-| 3 | Cluster stats endpoint 404 | Low | Missing Implementation |
-| 4 | Kubeconfig endpoint 404 | Low | Missing Implementation |
-| 5 | Monitoring metrics endpoint 404 | Low | Missing Implementation |
-| 6 | Inconsistent list response format | Low | API Consistency |
-| 7 | auth/me returns empty tokens | Low | API Response Quality |
-
-**Passed Tests:**
-- Login/authentication ✓
-- Auth/me user info ✓
-- Cluster listing ✓
-- Cluster health check ✓
-- Registry listing ✓
-- Repository browsing (artifacts) ✓
-- Instance deployment (nginx chart) ✓
-- Instance status tracking (pending-install → deployed) ✓
-- Instance deletion (async, transitions to pending-delete then removed) ✓
-- Error handling for invalid repository ✓
-- Error handling for missing required fields ✓
-- Auth rejects invalid tokens ✓
-- Auth rejects missing tokens ✓
-- Instance cleanup confirmed ✓
diff --git a/docs/bugs-user-c.md b/docs/bugs-user-c.md
deleted file mode 100644
index 45164f9..0000000
--- a/docs/bugs-user-c.md
+++ /dev/null
@@ -1,109 +0,0 @@
-# QA Report: Permission Isolation & Multi-Tenancy Testing — test-user-c
-
-**Tester:** test-user-c (role: `user`)
-**Date:** 2026-05-11
-**Environment:** http://10.6.80.114:18080
-
-## Summary
-
-Test-user-c is a standard `user` role with namespace `ocdp-u-test-c`, workspace `71459030-7166-4c79-b53c-81c61da4c313`. Permissions follow the `manage_own` / `view` pattern — no admin-level permissions.
-
----
-
-## Test Results
-
-### 1. Login & Basic Access ✅
-
-| Test | Result | Notes |
-|------|--------|-------|
-| POST /auth/login | ✅ Pass | Token issued, role=`user`, workspace/namespace correctly assigned |
-| GET /auth/me | ✅ Pass | Returns correct user profile with permissions |
-| GET /clusters | ✅ Pass | Sees all `global_shared` clusters (k8s, k3s) |
-| GET /registries | ✅ Pass | Sees all `global_shared` registries (harbor) |
-
-### 2. Admin Endpoint Protection
-
-| Test | Result | Notes |
-|------|--------|-------|
-| GET /api/v1/users | ✅ **403 Forbidden** | Properly blocked — `permission denied` |
-| POST /auth/register | ✅ **403 Forbidden** | Cannot register new users as non-admin |
-| GET /api/v1/admin/* | ✅ **404** | Admin route prefix doesn't exist (not a bypass risk) |
-
-### 3. Frontend Access
-
-| Test | Result | Notes |
-|------|--------|-------|
-| GET /configuration/users | ⚠️ **200 (OK)** | SPA returns index.html — expected. Auth is enforced via API, not routes. |
-| GET /configuration/clusters | ⚠️ **200 (OK)** | Same — SPA behavior. |
-| GET /configuration/registries | ⚠️ **200 (OK)** | Same. |
-
-**Risk: Low.** This is standard SPA behavior. Authorization is enforced at the API level. However, if the frontend relies solely on hiding UI elements rather than checking permissions, users who manually navigate could see empty/error states.
-
-### 4. Namespace Isolation Enforcement
-
-| Test | Result | Notes |
-|------|--------|-------|
-| Deploy with `namespace: ocdp-u-test-a` | ⚠️ **Silently overridden** | Server ignored requested namespace and used `ocdp-u-test-c` instead. **No warning or error returned.** |
-| PATCH to change namespace | ✅ **404** | PATCH endpoint doesn't exist — namespace cannot be changed after creation |
-
-🔴 **Bug: Silent namespace override (Low severity)**
-When a user specifies a namespace that doesn't belong to them in the instance creation request, the server silently overrides it with the user's own namespace. This is secure (prevents cross-namespace deployment) but:
-- The user receives HTTP 200 with the overridden value — no indication that their request was modified
-- The response does not differentiate between "user's own namespace" and "requested namespace"
-- This could lead to user confusion about where their resources were actually deployed
-- It's unclear whether the user's Helm values also get silently overridden (e.g., the `values.namespace` field)
-
-### 5. Resource Isolation
-
-| Test | Result | Notes |
-|------|--------|-------|
-| GET instances with other workspaceId query param | ✅ **Isolated** | Returns only own instances (workspaceId filter is server-enforced) |
-| DELETE on own instance | ⚠️ **Async deletion** | Returns HTTP 404 on DELETE itself, but instance transitions to `pending-delete` then disappears |
-
-🔴 **Bug: DELETE returns 404 on success (Medium severity)**
-When deleting an instance via `DELETE /clusters/{clusterId}/instances/{instanceId}`:
-- The instance transitions to `pending-delete` status
-- But the HTTP response status code is **404** rather than 200/202/204
-- The first raw DELETE call returns an empty body (causing JSON parse errors)
-- This is an API inconsistency — async deletions should return HTTP 202 Accepted
-
-### 6. Monitoring & Other Endpoints
-
-| Test | Result | Notes |
-|------|--------|-------|
-| GET /monitoring/clusters/.../pods | ✅ **404** | Monitoring endpoints not implemented for this cluster type |
-| POST /kubeconfig | ✅ **404** | Kubeconfig endpoint not implemented |
-
-These endpoints return 404 which is acceptable behavior for features not yet implemented.
-
----
-
-## Security Assessment
-
-### Works as Intended ✅
-- Admin endpoints (`/users`, `/auth/register`) properly return 403
-- User cannot access other users' instances via workspaceId manipulation
-- User cannot deploy into other users' Kubernetes namespaces
-- No PATCH/PUT verbs available to modify existing instance namespaces
-- No admin-specific route paths leak data
-
-### Bugs Found
-
-1. **DELETE returns 404 on successful async deletion** (Medium)
- - Endpoint: `DELETE /clusters/{id}/instances/{id}`
- - After call, instance status becomes `pending-delete` and eventually disappears
- - But the HTTP response is `404` with empty body
- - Expected: `202 Accepted` with a `status: "deleted"` or similar response
- - Risk: Clients interpreting HTTP 404 as "not found" will retry or report errors incorrectly
-
-2. **Silent namespace override without user feedback** (Low)
- - Endpoint: `POST /clusters/{id}/instances`
- - When requesting deployment into another user's namespace, the server silently uses the caller's namespace
- - No warning, no error, no indication in the response
- - Expected: Either `403 Forbidden` with "cannot deploy into namespace owned by another user" or a response field indicating the override occurred
- - Risk: Low for security (the override correctly prevents cross-tenant deployment), but could cause user confusion
-
-### No Critical Vulnerabilities Found
-- No privilege escalation vectors identified
-- No data leakage across workspaces
-- No ability to access or manipulate other users' resources
diff --git a/docs/regression-full-report.md b/docs/regression-full-report.md
new file mode 100644
index 0000000..d7e4484
--- /dev/null
+++ b/docs/regression-full-report.md
@@ -0,0 +1,209 @@
+# OCDP 回归测试完整报告 (Round 3)
+
+**测试日期:** 2026-05-14
+**环境:** http://10.6.80.114:18080
+**集群:** k3s v1.28.0 (dbf824f1-9962-4d8e-881e-870c75fdb6f5), k8s (23880994-dfe4-48d0-abc0-b49692cc630a)
+**Harbor:** harbor.bwgdi.com (83b823af-873b-457c-912c-9ccde3cb12e6)
+
+---
+
+## 代码变更概要 (Commit b88fe24 +)
+
+| Commit | 变更 |
+|--------|------|
+| b88fe24 | fix: real K8s replicas in list API, full Helm values in modify YAML editor |
+| 96d42ee | fix: scale replicas in response, YAML lineWidth, delta values, modified keys |
+| 4441f58 | fix: direct K8s scaling, replicas from K8s API, button labels, modify fetch |
+| 49b92e6 | fix: UI redesign — horizontal instance rows, proper scaling, readable tag cards |
+| 28ecb2e | feat: scale instances, --reuse-values, values diff, UI redesign, hover animations |
+| 87eaaa5 | fix: remove per-card Refresh button, consolidate to page-level refresh |
+
+---
+
+## 测试结果总览
+
+### 总评分: 85/100
+
+| 测试领域 | 状态 | 问题数 |
+|----------|------|--------|
+| 前端 UI / Launch / Routes | ✅ ALL PASS | 0 |
+| API CRUD / 部署生命周期 | ⚠️ 2 issues | 2 |
+| 配额执行 | ⚠️ 1 issue (pre-existing) | 1 |
+| Values 优先级 / gpuMem | ⚠️ 1 issue | 1 |
+| 权限隔离 | ✅ ALL PASS | 0 |
+| 安全测试 | ⚠️ 3 issues (pre-existing) | 3 |
+| DELETE 行为 | ✅ FIXED | 0 |
+| InstanceCard / Scaling UI | ✅ ALL PASS | 0 |
+| ModifyModal / Values Diff | ✅ ALL PASS | 0 |
+
+---
+
+## 新发现 Bug
+
+### 🆕 BUG-R3-001: Detail API replicas=0 与 List API 不一致 (High)
+
+| 属性 | 值 |
+|------|-----|
+| 严重度 | **P1 (High)** |
+| 端点 | `GET /clusters/{id}/instances/{instance_id}` |
+| 现象 | List API 返回正确 replicas(如 1, 5),但 Detail API 始终返回 `replicas: 0` |
+| 根因 | Detail endpoint 从数据库读取实例记录,replicas 字段未同步自 K8s 实时状态 |
+| 影响 | 前端依赖 Detail API 的页面(如刷新后详情页)显示错误的副本数 |
+| 修复建议 | Detail endpoint 也从 K8s live state 填充 replicas,或确保数据库同步 |
+
+### 🆕 BUG-R3-002: List API 移除 values 字段
+
+| 属性 | 值 |
+|------|-----|
+| 严重度 | **P2 (Medium)** |
+| 端点 | `GET /clusters/{id}/instances` |
+| 现象 | List API 响应不再包含 `values` 字段(之前版本有);values 仅在单实例 GET 中返回 |
+| 影响 | 依赖 list API values 的测试脚本和前端组件会 break |
+| 备注 | 可能是故意的性能优化,但属于 API 行为变更 |
+
+---
+
+## 已修复 Bug (验证通过)
+
+| Bug ID | 描述 | 之前状态 | 现在状态 | 验证 |
+|--------|------|----------|----------|------|
+| BUG-001 | Launch 按钮无反应 | P0 Blocker | ✅ FIXED | 部署端到端成功 |
+| BUG-002 | SPA 旧路由空白页 | P0 Blocker | ✅ FIXED | 所有旧路由返回 SPA |
+| BUG-003 | DELETE 返回 404 | P1 High | ✅ FIXED | 返回 HTTP 204 |
+| BUG-004 | DELETE 空 body | P1 High | ✅ FIXED | HTTP 204 No Content |
+| - | InstanceCard 无 scaling UI | New Feature | ✅ ADDED | +/- 按钮 + API 调用 |
+| - | ModifyModal values 为空 | Bug | ✅ FIXED | Full Helm values 返回 |
+
+---
+
+## 仍未修复的已知问题
+
+| Bug ID | 描述 | 严重度 | 状态 |
+|--------|------|--------|------|
+| BUG-013 | 用户枚举(login 错误消息差异) | Medium | 未修复 |
+| BUG-014 | 无速率限制 | Medium | 未修复 |
+| BUG-015 | Nginx 版本泄露 | Low | 未修复 |
+| BUG-016 | CORS: * | Low | 未修复 |
+| BUG-017 | 缺少安全响应头 | Low | 未修复 |
+| BUG-018 | /health 返回 HTML | Low | 未修复 |
+| BUG-005 | Tags 端点 404 | Medium | 未修复 |
+| BUG-006 | Namespace 静默覆盖无警告 | Medium | 未修复 |
+| BUG-007-009 | Metrics/Stats/Kubeconfig 端点缺失 | Medium | 未修复 |
+| BUG-011 | API 响应格式不一致 | Low | 未修复 |
+| BUG-012 | /auth/me 空 token 字段 | Low | 未修复 |
+| - | API 层配额预检查 | New Feature | 未实现 |
+| - | Values 冲突时无警告 | UX | 未修复 |
+
+---
+
+## 详细测试结果
+
+### 1. 前端 UI (test-user-a)
+
+| 测试项 | 结果 |
+|--------|------|
+| 登录页加载 | ✅ HTTP 200, SPA 398 bytes |
+| JWT 认证 | ✅ role=user, 10 permissions, quota 字段 |
+| 所有页面路由 | ✅ 6 个路由全部返回 398 bytes (非空白) |
+| 旧路由重定向 | ✅ /clusters, /registries, /launch, /monitoring 全部返回 SPA |
+| Chart Browser | ✅ nginx:22.1.1 可发现,Launch 按钮可用 |
+| 部署 Pipeline | ✅ pending-install → deployed (~15s) |
+| InstanceCard Scale UI | ✅ +/- 按钮,副本数显示 |
+| InstanceCard Actions | ✅ Entries/Diag/Modify/Delete 全部可见 |
+| ModifyModal YAML Editor | ✅ full Helm values, lineWidth:0, diff 检测 |
+| TagCard UI | ✅ 色标 (chart=blue, image=green), Copy helm pull 命令, LATEST badge |
+| DELETE | ✅ HTTP 204 |
+
+### 2. API 后端 (test-user-b)
+
+| 测试项 | 结果 |
+|--------|------|
+| Login + /auth/me | ✅ 完整 profile (quota, namespace, permissions) |
+| Cluster list | ✅ 2 clusters |
+| Registry list | ✅ 1 registry (harbor-bwgdi) |
+| Repository artifacts | ✅ Harbor API proxy 正常 |
+| Cluster health | ✅ k3s healthy, v1.28.0 |
+| 部署 nginx (default) | ✅ deployed, replicas=1 |
+| 部署 nginx (over-quota) | ⚠️ 接受部署 (API 无预检查) |
+| 实例状态轮询 | ✅ ~20s 到达 deployed |
+| List API replicas | ✅ 正确显示 1/5 |
+| Detail API replicas | ❌ 返回 0 (BUG-R3-001) |
+| 实例删除 | ✅ HTTP 204 (BUG-003 FIXED) |
+| 缺失端点 | ✅ 正常 404 |
+
+### 3. Values 优先级 (test-user-c)
+
+| 方法 | 结果 |
+|------|------|
+| values JSON only | ✅ gpuMem=10000 正确接受 |
+| valuesYaml only | ✅ YAML -> JSON 解析正确 |
+| 同时提供 (冲突) | ✅ values JSON 覆盖 valuesYaml |
+| 冲突警告 | ❌ 无警告(建议添加) |
+| 默认值 (空 values) | ✅ 使用 chart 内置 defaults |
+| gpuMem=10000 | ✅ 整数 MB scalar 正确 |
+| 清理 | ✅ 所有 3 个实例已删除 |
+
+### 4. 权限隔离
+
+| 测试项 | 结果 |
+|--------|------|
+| Admin lists users | ✅ 10 users |
+| test-admin-d lists users | ✅ 10 users (admin role works) |
+| test-user-c GET /users | ✅ 403 Forbidden |
+| test-user-c POST /auth/register | ✅ 403 Forbidden |
+| Cross-tenant deploy (c→b) | ✅ Silent override to ocdp-u-test-c (secure) |
+| Deploy into own namespace | ✅ 成功 |
+| Instance visibility | ✅ 仅看到自己的实例 |
+| Disable user | ✅ 用户被禁用 |
+| Disabled user login | ✅ 401 |
+| Re-enable user | ✅ 恢复 |
+| Self-registration | ✅ 401 (必须认证) |
+
+### 5. 安全 (回归)
+
+| 测试项 | 结果 |
+|--------|------|
+| 未认证端点 | ✅ 全部 401 |
+| JWT 验证 | ✅ 篡改 token 被拒绝 |
+| XSS/SQLi | ✅ 安全处理 |
+| 敏感数据脱敏 | ✅ creds 显示为 •••••••• |
+| 普通用户权限提升 | ✅ 403 blocked |
+| 用户枚举 | ❌ 不同错误消息 (未修复) |
+| 速率限制 | ❌ 无 429 (未修复) |
+| CORS * + 缺失安全头 | ❌ 未修复 |
+| Nginx 版本泄露 | ❌ "nginx/1.27.5" (未修复) |
+| /auth/me 空 token | ✅ tokens 已正确清空 |
+
+---
+
+## 优先修复清单
+
+### 立即修复 (P0/P1)
+1. **BUG-R3-001**: Detail API replicas=0 — 从 K8s live state 填充
+2. **配额预检查** — POST instances 时验证请求资源 ≤ 用户配额
+
+### 尽快修复 (P2)
+3. Values 冲突时添加 API 警告
+4. Tags/Metrics/Stats/Kubeconfig 端点实现
+5. Namespace 静默覆盖返回警告
+6. List API values 字段回归或文档化
+
+### 安全加固 (P3)
+7. 登录错误消息统一
+8. 速率限制
+9. Nginx 安全头 + 关闭 server_tokens
+10. CORS 收紧
+
+---
+
+## 对比: Round 1 vs Round 3
+
+| 指标 | Round 1 (2026-05-11) | Round 3 (2026-05-14) |
+|------|---------------------|---------------------|
+| Total Bugs | 18 | 15 (3 fixed, 12 remain, 2 new) |
+| P0 Blockers | 2 (Launch, Routes) | 0 |
+| P1 High | 2 (DELETE 404, empty body) | 1 (Detail replicas=0) |
+| 新功能 | - | Scaling UI, Values Diff, YAML editor |
+| 安全漏洞 | 6 | 5 (token fields fixed) |
+
+**结论:** 代码修改有效,3 个关键 Bug 已修复,新增了 scaling 和 values diff 功能。仍有 12 个已知问题和 2 个新问题待修复。
diff --git a/docs/security/bugs-security.md b/docs/security/bugs-security.md
deleted file mode 100644
index bdba1a2..0000000
--- a/docs/security/bugs-security.md
+++ /dev/null
@@ -1,284 +0,0 @@
-# OCDP Security Audit Report
-
-**Date:** 2026-05-11
-**Target:** http://10.6.80.114:18080
-**API Base:** http://10.6.80.114:18080/api/v1
-
----
-
-## Finding 1: User Enumeration via Login Error Messages
-
-| Field | Value |
-|-------|-------|
-| **Test** | Authentication Error Disclosure |
-| **Severity** | **Medium** |
-| **Endpoint** | `POST /api/v1/auth/login` |
-| **Status** | Confirmed |
-
-### What I Did
-
-```bash
-# Non-existent user
-curl -s -X POST http://10.6.80.114:18080/api/v1/auth/login \
- -H "Content-Type: application/json" \
- -d '{"username":"nonexistent_user_xyz","password":"test123"}'
-
-# Existing user with wrong password
-curl -s -X POST http://10.6.80.114:18080/api/v1/auth/login \
- -H "Content-Type: application/json" \
- -d '{"username":"admin","password":"wrongpassword"}'
-```
-
-### Expected
-
-Both requests should return the same generic error message (e.g., "Invalid credentials") to prevent username enumeration.
-
-### Actual
-
-- Non-existent user: `{"error":"Login failed","message":"user not found","code":401}`
-- Existing user: `{"error":"Login failed","message":"invalid password","code":401}`
-
-The error messages are different, allowing an attacker to determine whether a username exists in the system.
-
-### Impact
-
-An attacker can enumerate valid usernames by observing the error message difference. This is the first step in a targeted brute force or credential stuffing attack.
-
-### Recommendation
-
-Return identical error messages for both cases, e.g., `"Invalid username or password"`.
-
----
-
-## Finding 2: No Rate Limiting on Login Endpoint
-
-| Field | Value |
-|-------|-------|
-| **Test** | Brute Force Protection |
-| **Severity** | **Medium** |
-| **Endpoint** | `POST /api/v1/auth/login` |
-| **Status** | Confirmed |
-
-### What I Did
-
-```bash
-for i in $(seq 1 10); do
- curl -s -o /dev/null -w "%{http_code}" \
- -X POST http://10.6.80.114:18080/api/v1/auth/login \
- -H "Content-Type: application/json" \
- -d '{"username":"admin","password":"wrongpassword"}'
-done
-```
-
-### Expected
-
-After a threshold (e.g., 5 failed attempts), the server should return HTTP 429 Too Many Requests or temporarily lock the account.
-
-### Actual
-
-All 10 rapid sequential attempts returned HTTP 401. No rate limiting, no account lockout, no progressive delay.
-
-### Impact
-
-An attacker can brute force passwords without restriction. Combined with Finding 1 (user enumeration), the attack surface is increased.
-
-### Recommendation
-
-- Implement rate limiting on the login endpoint (e.g., max 5 attempts per minute per IP).
-- Consider account lockout after N failed attempts.
-- Add progressive response delays after repeated failures.
-
----
-
-## Finding 3: Server Version Disclosure
-
-| Field | Value |
-|-------|-------|
-| **Test** | Information Disclosure |
-| **Severity** | **Low** |
-| **Endpoint** | All (HTTP response headers) |
-| **Status** | Confirmed |
-
-### What I Did
-
-```bash
-curl -s -D - http://10.6.80.114:18080/ | head -10
-```
-
-### Expected
-
-Server header should be generic (e.g., `Server: nginx`) or removed entirely.
-
-### Actual
-
-```http
-Server: nginx/1.27.5
-```
-
-### Impact
-
-Knowing the exact nginx version helps attackers target known vulnerabilities for that specific version.
-
-### Recommendation
-
-Disable or obfuscate the Server header in nginx configuration:
-
-```nginx
-server_tokens off;
-```
-
----
-
-## Finding 4: Permissive CORS Policy
-
-| Field | Value |
-|-------|-------|
-| **Test** | CORS Misconfiguration |
-| **Severity** | **Low** |
-| **Endpoint** | All API endpoints |
-| **Status** | Confirmed |
-
-### What I Did
-
-```bash
-curl -s -D - http://10.6.80.114:18080/api/v1/auth/login \
- -X POST -H "Content-Type: application/json" \
- -d '{"username":"test","password":"test"}'
-```
-
-### Expected
-
-CORS `Access-Control-Allow-Origin` should be restricted to the application's origin (e.g., `http://10.6.80.114:18080`) rather than allowing all origins.
-
-### Actual
-
-```http
-Access-Control-Allow-Origin: *
-Access-Control-Allow-Credentials: true
-Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
-Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
-Access-Control-Max-Age: 86400
-```
-
-### Impact
-
-Any website can make cross-origin requests to the API. If a user is logged in, a malicious site could potentially make authenticated API calls on their behalf (CSRF-style attack, though mitigated by the Bearer token requirement).
-
-### Recommendation
-
-Restrict `Access-Control-Allow-Origin` to the specific frontend origin(s) instead of `*`.
-
----
-
-## Finding 5: Missing Security Headers
-
-| Field | Value |
-|-------|-------|
-| **Test** | Security Headers Audit |
-| **Severity** | **Low** |
-| **Endpoint** | All |
-| **Status** | Confirmed |
-
-### What I Did
-
-```bash
-curl -s -D - http://10.6.80.114:18080/ | head -20
-```
-
-### Expected
-
-Security headers should include:
-- `Strict-Transport-Security`
-- `X-Content-Type-Options: nosniff`
-- `X-Frame-Options: DENY`
-- `Content-Security-Policy`
-
-### Actual
-
-None of these security headers are present in responses.
-
-### Impact
-
-Increases attack surface for clickjacking, MIME-type confusion, and XSS attacks.
-
-### Recommendation
-
-Add the following headers to nginx configuration:
-
-```
-add_header X-Frame-Options "DENY" always;
-add_header X-Content-Type-Options "nosniff" always;
-add_header X-XSS-Protection "0" always;
-add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
-add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;
-```
-
----
-
-## Finding 6: `/health` Endpoint Returns HTML Instead of Health Status
-
-| Field | Value |
-|-------|-------|
-| **Test** | Health Endpoint Behavior |
-| **Severity** | **Low** |
-| **Endpoint** | `GET /health` |
-| **Status** | Confirmed |
-
-### What I Did
-
-```bash
-curl -s http://10.6.80.114:18080/health
-```
-
-### Expected
-
-A health check endpoint should return a structured JSON response (e.g., `{"status":"healthy"}`) with HTTP 200.
-
-### Actual
-
-Returns the full `index.html` SPA page with HTTP 200:
-
-```html
-
-
-
- OCDP Platform
- ...
-```
-
-### Impact
-
-Not a direct vulnerability, but misconfigured health checks can cause false positives in monitoring/load balancer health checks. It also means the SPA is served at `/health`, which is unexpected.
-
-### Recommendation
-
-Implement a dedicated health endpoint that returns `{"status":"ok"}` with appropriate content type, or remove the `/health` route if not needed.
-
----
-
-## Tests Passed (No Issues Found)
-
-| Test | Result |
-|------|--------|
-| **1. Unauthenticated Access** | **PASS** - All business endpoints return 401 |
-| **2. JWT Token Manipulation** | **PASS** - Tampered tokens, alg=none, invalid formats all rejected (401) |
-| **3. XSS/SQLi Testing** | **PASS** - Script injection, SQLi patterns safely handled |
-| **4. IDOR - Instance Access** | **PASS** - No instances deployed to test; cluster/registry isolation confirmed working |
-| **5. Sensitive Data Masking** | **PASS** - Cluster certs/keys and registry passwords masked as `••••••••` |
-| **6. Self-Registration** | **PASS** - Registration endpoint requires authentication (401) |
-| **7. Path Traversal** | **PASS** - Path traversal attempts return index.html (not /etc/passwd) |
-| **8. Admin Permission Escalation** | **PASS** - Regular users blocked from admin endpoints (403) |
-
----
-
-## Summary
-
-| Severity | Count | Findings |
-|----------|-------|----------|
-| Critical | 0 | — |
-| High | 0 | — |
-| **Medium** | **2** | User enumeration, No rate limiting |
-| **Low** | **4** | Server version disclosure, Permissive CORS, Missing security headers, `/health` returns HTML |
-| **Total** | **6** | |
-
-The platform's core security controls (authentication, JWT validation, authorization, sensitive data masking) are properly implemented. The main areas for improvement are authentication hardening (rate limiting, user enumeration) and HTTP security hardening (headers, CORS).
diff --git a/docs/test2-quota.md b/docs/test2-quota.md
deleted file mode 100644
index 2479c77..0000000
--- a/docs/test2-quota.md
+++ /dev/null
@@ -1,156 +0,0 @@
-# Resource Quota Enforcement Test Report
-
-**Date:** 2026-05-11
-**Tester:** test-user-b
-**Namespace:** ocdp-u-test-b
-**User Quota:** cpu=2, memory=4Gi, gpu=0, gpumem=0
-
----
-
-## Test Summary
-
-| Test | Description | Expected | Actual | Result |
-|------|-------------|----------|--------|--------|
-| A | Deploy nginx (default, within quota) | Success | Deployed (status: `deployed`) | ✅ PASS |
-| B | Deploy nginx (cpu=4, mem=8Gi, replicas=5, exceeds quota) | Blocked by quota | Helm release created, Service created, all pods blocked by ResourceQuota (status: `pending-install`) | ⚠️ PARTIAL |
-| C | Deploy vllm-serve with gpu=1 (gpu quota = 0) | Blocked by quota | Helm release created, all pods blocked by ResourceQuota (status: `pending-install`) | ⚠️ PARTIAL |
-
----
-
-## Detailed Results
-
-### Test A: Deploy nginx within quota limits
-
-- **Instance:** `quota-test-nginx` (ed846c33-3631-4d54-adce-c7f00210176f)
-- **Chart:** charts/nginx:22.1.1
-- **Values:** defaults
-- **API Response:** HTTP 200, status: `pending-install`
-- **Final Status after 21s:** `deployed` ("Instance deployed successfully")
-- **K8s Resource Usage:** requests.cpu=100m/2, requests.memory=128Mi/4Gi
-
-### Test B: Deploy nginx exceeding quota
-
-- **Instance:** `quota-test-nginx-2` (36c0350f-089c-41c2-a66e-e93539c00d52)
-- **Chart:** charts/nginx:22.1.1
-- **Values:** replicaCount=5, resources.limits.cpu=4/memory=8Gi, resources.requests.cpu=2/memory=4Gi
-- **API Response:** HTTP 200, status: `pending-install`
-- **Final Status (observed for 90s+):** `pending-install` (never transitioned to `deployed` or `failed`)
-- **K8s Behavior:**
- - Helm release created: `sh.helm.release.v1.quota-test-nginx-2.v1`
- - TLS secret created
- - Service created, IP assigned
- - Deployment created, ReplicaSet scaled up
- - **All pod creations FAILED** with: `Error creating: pods "..." is forbidden: exceeded quota: tenant-quota, requested: requests.cpu=2,requests.memory=4Gi, used: requests.cpu=100m,requests.memory=128Mi, limited: requests.cpu=2,requests.memory=4Gi`
-
-### Test C: Deploy GPU instance (gpu quota = 0)
-
-- **Instance:** `quota-test-gpu` (a0d692c8-cdf8-4248-a6d4-1468ad4a7cc7)
-- **Chart:** charts/vllm-serve:0.6.0
-- **Values:** resources.gpuLimit=1, resources.gpuMem=5000
-- **API Response:** HTTP 200, status: `pending-install`
-- **Final Status (observed for 30s+):** `pending-install`
-- **K8s Behavior:**
- - vllm-serve chart defaults: requests.cpu=8, requests.memory=16Gi, requests.nvidia.com/gpu=1, requests.nvidia.com/gpumem=5k
- - All pods blocked: `exceeded quota: tenant-quota, requested: requests.cpu=8,requests.memory=16Gi,requests.nvidia.com/gpu=1,..., limited: requests.cpu=2,requests.memory=4Gi,requests.nvidia.com/gpu=0`
-
----
-
-## Key Findings
-
-### 1. No API-Level (Pre-flight) Quota Enforcement
-
-The backend API accepts **all** deployment requests regardless of whether they exceed the user's quota. There is no validation at the API layer that checks:
-
-- Whether the requested resources exceed the user's quota limits
-- Whether the user's quota is already fully consumed by existing deployments
-
-**Evidence:** All three deployments returned HTTP 200 with `status: pending-install`. The backend logs contain zero quota-related entries.
-
-### 2. Kubernetes ResourceQuota Enforces at Pod Level
-
-The Kubernetes `ResourceQuota` object `tenant-quota` in namespace `ocdp-u-test-b` does enforce limits, but only at the **pod creation** level:
-
-```yaml
-spec:
- hard:
- requests.cpu: "2"
- requests.memory: 4Gi
- requests.nvidia.com/gpu: "0"
- requests.nvidia.com/gpumem: "0"
-```
-
-When pods exceed quota, Kubernetes explicitly refuses to create them with a clear error message.
-However, Helm releases, Services, Deployments, and ReplicaSets are **still created** even when pods are blocked.
-
-### 3. Stuck at "pending-install"
-
-Instances that exceed quota remain stuck in `pending-install` status **indefinitely** — they never transition to `deployed`, `failed`, or any error status. The OCDP platform does not detect the ResourceQuota rejection and update the instance status accordingly. The only way to know about the failure is to check Kubernetes events directly:
-
-```bash
-kubectl get events -n ocdp-u-test-b
-```
-
-### 4. GPU Quota Enforcement
-
-Users with `gpu=0` quota **can** submit deployments referencing GPU-enabled charts. The API does not reject them. Only the K8s ResourceQuota blocks pod creation at runtime. This could lead to:
-- Unnecessary Helm releases and resource overhead in the cluster
-- Confusion for users whose deployments appear to hang at `pending-install`
-
-### 5. Quota Exposed in Login Response
-
-The login response includes quota information:
-```json
-{
- "quotaCpu": "2",
- "quotaMemory": "4Gi",
- "quotaGpu": "0",
- "quotaGpuMemory": "0"
-}
-```
-This could be used by the frontend to show usage limits, but no pre-flight check uses it server-side.
-
----
-
-## Recommendations
-
-1. **Add pre-flight quota validation** in the backend API: before accepting a deployment, check whether the requested resources (from chart values) would exceed the user's quota. Return HTTP 4xx with a clear error message.
-
-2. **Handle "pending-install" timeout**: implement a watcher that detects when a Helm release has been created but pods remain stuck (e.g., due to ResourceQuota) and:
- - Update instance status to `failed` with a descriptive `statusReason`
- - Clean up the Helm release, Service, etc.
- - Optionally surface the K8s error message via the API
-
-3. **GPU quota pre-check**: if a chart requests GPU resources and the user's `gpu=0`, reject the deployment at the API level before creating any Kubernetes resources.
-
-4. **UI quota indicator**: show remaining quota (used vs. hard limit) on the deployment form so users know their limits before submitting.
-
----
-
-## ResourceQuota YAML (for reference)
-
-```yaml
-apiVersion: v1
-kind: ResourceQuota
-metadata:
- name: tenant-quota
- namespace: ocdp-u-test-b
- labels:
- ocdp.io/managed-by: ocdp
- ocdp.io/tenant: ocdp-u-test-b
-spec:
- hard:
- requests.cpu: "2"
- requests.memory: 4Gi
- requests.nvidia.com/gpu: "0"
- requests.nvidia.com/gpumem: "0"
-```
-
----
-
-## Cleanup Verification
-
-All test instances were removed after testing:
-- `quota-test-nginx` ✅ deleted (pods terminated, helm release removed, quota back to 0)
-- `quota-test-nginx-2` ✅ cleaned up (no pods created, resources released)
-- `quota-test-gpu` ✅ cleaned up (no pods created, resources released)
-- ResourceQuota used: all resources at 0
diff --git a/docs/test2-report.md b/docs/test2-report.md
deleted file mode 100644
index 09f674f..0000000
--- a/docs/test2-report.md
+++ /dev/null
@@ -1,141 +0,0 @@
-# OCDP 第二次测试报告
-
-**测试日期:** 2026-05-11
-**测试环境:** http://10.6.80.114:18080
-
----
-
-## 测试1: 资源配额限额
-
-### 测试方法
-使用 test-user-b(quota: cpu=2, mem=4Gi, gpu=0, gpumem=0)在 k3s 集群部署 nginx chart
-
-### 测试结果
-
-| 测试 | 操作 | 预期 | 实际 | 结论 |
-|------|------|------|------|------|
-| Test A | 部署 nginx(默认值,在配额内) | 成功 | 部署完成,状态 deployed | ✅ |
-| Test B | 部署 nginx(requests.cpu=2, mem=4Gi, replica=5,超配额) | 被配额阻止 | Helm release 创建成功,所有 Pod 被 ResourceQuota 阻塞,状态永远 stuck 在 pending-install | ⚠️ 部分通过 |
-| Test C | 部署 vllm-serve(gpuLimit=1,gpu配额=0) | 被配额阻止 | Helm release 创建成功,Pod 被 ResourceQuota 阻塞,状态 pending-install | ⚠️ 部分通过 |
-
-### 关键发现
-
-**1. 没有 API 层的预检查配额验证**
-- 后端 API 无条件接受所有部署请求,不检查是否超配额
-- 所有超配额请求返回 HTTP 200 + status: pending-install
-- 后端日志中**没有任何配额相关的条目**
-
-**2. K8s ResourceQuota 在 Pod 级别执行**
-- `tenant-quota` ResourceQuota 对象确实存在并执行限制
-- 当 Pod 超配额时,K8s 明确拒绝创建并给出错误消息
-- 但 Helm release、Service、Deployment、ReplicaSet **仍然被创建**
-
-**3. 实例永远 stuck 在 "pending-install"**
-- 超配额的实例永远不会转换到 deployed/failed/error
-- OCDP 平台不检测 ResourceQuota 拒绝事件
-- 唯一知道失败的方式是直接查 K8s events
-
-**4. GPU 配额绕过**
-- gpu=0 的用户可以提交需要 GPU 的 chart 部署
-- K8s ResourceQuota 最终会阻止,但 Helm release 等资源已被创建
-
-**5. 有效的 ResourceQuota 配置**
-```yaml
-apiVersion: v1
-kind: ResourceQuota
-metadata:
- name: tenant-quota
- namespace: ocdp-u-test-b
-spec:
- hard:
- requests.cpu: "2"
- requests.memory: 4Gi
- requests.nvidia.com/gpu: "0"
- requests.nvidia.com/gpumem: "0"
-```
-
-### 建议
-1. **添加 API 层预检查配额验证** — 在接受部署前检查请求资源是否超过用户配额
-2. **处理 pending-install 超时** — 监控 Helm release 创建后 Pod 是否 stuck,更新状态为 failed
-3. **GPU 配额预检查** — 如果 chart 需要 GPU 而用户 gpu=0,在 API 层拒绝
-4. **UI 配额指示器** — 在部署表单上显示剩余配额
-
----
-
-## 测试2: values.yaml 覆盖优先级
-
-### 测试方法
-使用 test-user-c(quota: cpu=4, mem=8Gi, gpu=1, gpumem=5000)部署 vllm-serve:0.6.0 chart
-
-### 测试结果
-
-| 方法 | 提交方式 | 是否部署成功 | 存储的值 | 结论 |
-|------|----------|-------------|---------|------|
-| 方法1 | `values` JSON 字段 | ✅ | cpuRequest=2, gpuMem=10000 | JSON 值被准确接受和存储 |
-| 方法2 | `valuesYaml` 字符串 | ✅ | cpuRequest=4, gpuMem=10000 | YAML 被正确解析为结构化 values |
-| 方法3 | 同时提供 `values` + `valuesYaml`(冲突) | ✅ 无任何错误/警告 | **values JSON 全胜** | `values` JSON 静默覆盖 `valuesYaml` |
-| 方法4 | 不提供任何 values(使用 chart 默认) | ✅ | 仅 namespace | chart 默认值不存储在 API 响应中 |
-
-### 优先级最终结论
-
-| 优先级 | 来源 | 说明 |
-|--------|------|------|
-| **最高** | `values` JSON 字段 | 请求体中的结构化 JSON |
-| **中** | `valuesYaml` 字符串 | 请求体中的 YAML 字符串 |
-| **最低** | Chart 内置 values.yaml | Helm chart 打包的默认值 |
-
-### 冲突测试详细结果
-
-当同时提供 `values` 和 `valuesYaml` 且值冲突时:
-- `values` JSON 字段**完全覆盖** `valuesYaml`
-- **没有任何错误或警告**返回给用户
-- 两者被合并到统一的 DB `values` 字段
-
-### gpuMem=10000 行为
-- 整数值 `10000` 在 `values` JSON 和 `valuesYaml` 中都被**正确接受**
-- 无单位转换(作为整数 MB 标量存储)
-- 符合项目规范
-
-### 建议
-1. **记录优先级顺序** — 用户需知道同时提供两者时 values JSON 优先
-2. **添加冲突警告** — 当两个字段存在冲突值时应返回警告
-3. **考虑废弃一个字段** — values 和 valuesYaml 语义重复易混淆
-
----
-
-## 测试3: 前端 UI 溢出/滚动/刷新
-
-### 测试方法
-Playwright + 源码分析,测试 1920/768/375 三个视口
-
-### 测试结果
-**总体结论: PASS** — 没有导致功能问题的关键溢出问题
-
-| 测试项 | 结果 | 详情 |
-|--------|------|------|
-| 水平溢出 | ✅ 无问题 | 所有视口均无水平溢出 |
-| 文本截断 | ⚠️ 1 个低风险 | InstanceCard h3 标题 truncate 无 title tooltip |
-| 响应式设计 | ✅ 正确 | sm/md/lg/xl 断点覆盖完整 |
-| 滚动行为 | ✅ 流畅 | Sidebar 和内容区独立滚动,overscroll-contain 防滚动穿透 |
-| 模态框布局 | ✅ 正确 | body scroll lock + 内容独立滚动 |
-| 页面刷新 | ✅ 正常 | 受保护路由正确重定向到登录页 |
-| 颜色对比度 | ⚠️ 1 个中风险 | 登录页错误文本 red-400 在白色背景上仅 2.5:1 (WCAG AA 要求 4.5:1) |
-
-### 通过的细分项
-- Chart Browser 全高 + overflow-y-auto 布局 ✅
-- InstanceCard 操作按钮网格 grid-cols-2/3/5 响应正确 ✅
-- Tabs 支持 overflow-x-auto 水平滚动 ✅
-- 用户管理表格 overflow-x-auto ✅
-- iOS 触摸滚动 (`-webkit-overflow-scrolling: touch`) 已配置 ✅
-
-### 建议
-1. 将登录页错误文本从 text-red-400 改为 text-red-600/700
-2. InstanceCard h3 标题添加 title 属性
-
----
-
-## 综合建议
-1. 添加 API 层配额预检查
-2. 处理 pending-install 超时 + 状态更新
-3. 记录 values 覆盖优先级并添加冲突警告
-4. 统一 values JSON/YAML 的 API 设计
diff --git a/docs/test2-ui-overflow.md b/docs/test2-ui-overflow.md
deleted file mode 100644
index 12c25b8..0000000
--- a/docs/test2-ui-overflow.md
+++ /dev/null
@@ -1,271 +0,0 @@
-# QA Report: UI Layout Overflow & Responsiveness Test
-
-**Date:** 2026-05-11
-**Environment:** http://10.6.80.114:18080
-**Browser:** Chromium (Playwright headless)
-**Test Credentials:** test-user-a / TestUserA123!
-
----
-
-## Test Results Summary
-
-| # | Test | Status | Issues Found |
-|----|------|--------|-------------|
-| 1 | Login Page Layout | ✅ Pass | 1 Low |
-| 2 | Home Page | ✅ Pass | 0 |
-| 3 | Chart Browser (Registries) | ✅ Pass | 0 |
-| 4 | Instances Page | ✅ Pass | 0 |
-| 5 | Monitoring Page | ✅ Pass | 0 |
-| 6 | Tablet Responsive (768px) | ✅ Pass | 0 |
-| 7 | Mobile Responsive (375px) | ✅ Pass | 0 |
-| 8 | Deep DOM Overflow Analysis | ✅ Pass | 0 |
-| 9 | Source Code CSS Pattern Audit | ✅ Pass | 2 Info |
-| 10 | Text Visibility & Contrast | ⚠️ 1 Issue | 1 Medium |
-
----
-
-## 1. Login Page (AuthPage.tsx)
-
-**Location:** `frontend/src/features/auth/pages/AuthPage.tsx`
-
-**Layout:**
-- Form card is `max-w-md` (448px), horizontally centered via `flex items-center justify-center`
-- Desktop viewport (1920×1080): card is perfectly centered (checked via bounding rect)
-- Background: `bg-slate-50` with gradient overlay
-- Card: `bg-white/95 backdrop-blur-xl` with `shadow-2xl`
-
-**Responsive:**
-- Padding: `px-4 sm:px-6` — increases from 16px → 24px on `sm:` breakpoint
-- Card padding: `p-6 sm:p-7`
-- Icon: `w-11 h-11` — fixed size, not responsive
-
-### ✅ Issue #1-LOW: Login error text color contrast
-- **File:** `AuthPage.tsx:96`
-- **Pattern:** `
`
-- **Problem:** `text-red-400` (`#f87171`) on white background has a contrast ratio of ~2.5:1, which fails WCAG AA (minimum 4.5:1 for normal text). Error messages may be hard to read for users with visual impairments.
-- **Recommendation:** Use `text-red-600` or `text-red-700` for error text on white backgrounds.
-
----
-
-## 2. Home Page
-
-**Location:** `frontend/src/features/home/pages/HomePage.tsx`
-
-**Layout:**
-- Main container: `min-h-full bg-slate-50 px-4 py-6 sm:px-6 lg:px-8`
-- Two-column layout on large screens: `lg:grid-cols-[1.4fr_0.8fr]`
-- Feature cards: `md:grid-cols-3`
-- Quick actions: `md:grid-cols-3`
-
-**Scroll:** ScrollHeight=1080, Viewport=1080 — content fits exactly without scrolling on 1080p.
-
-**Overflow:** No horizontal overflow detected. Proper use of responsive padding and grid columns.
-
-### Passing — no issues found.
-
----
-
-## 3. Chart Browser / Registries
-
-**Location:** `frontend/src/features/artifact/registries/pages/ArtifactBrowserPage.tsx`
-
-**Layout (Desktop):**
-- Main layout: `flex-1 flex overflow-hidden bg-slate-50` (sidebar + detail panes)
-- Sidebar tree: `flex-1 overflow-y-auto custom-scrollbar`
-- Detail pane: `flex-1 flex flex-col bg-white overflow-hidden`
-- Tag grid: `grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4`
-
-**Tablet (768px):** No overflow. Grid collapses to 2 columns.
-
-**Mobile (375px):** No overflow. Grid collapses to 1 column.
-
-### Key Patterns Found:
-- `RepositoryItem.tsx:212` — `` — proper truncation with `title` tooltip
-- `ArtifactBrowserPage.tsx:336` — `
` — uses 11px text with truncation
-- `TagCard.tsx` — uses truncation with `title` attribute for long names
-
-### Passing — no overflow issues found.
-
----
-
-## 4. Instances Page
-
-**Location:** `frontend/src/features/artifact/instances/pages/InstancesManagementPage.tsx`
-**Component:** `InstanceCard.tsx`
-
-**Layout:**
-- Cluster cards: responsive grid `clusters.length > 1 ? 'md:grid-cols-3' : 'md:grid-cols-2'`
-- Instance cards listed in single column then `lg:grid-cols-2 gap-6`
-- Action buttons grid: `grid-cols-2 gap-2 md:grid-cols-3 xl:grid-cols-5`
-
-### ✅ Issue #2-INFO: Action button text truncation on InstanceCards
-- **File:** `InstanceCard.tsx:285-327`
-- **Pattern:**
- ```
-
-
-
- ```
-- **Analysis:** At `grid-cols-2` (small screens), two buttons share each row. The buttons use `min-w-0` which allows them to shrink, and `truncate` on the text span. However, the button text is short ("Refresh", "Entries", "Diagnostics", "Modify", "Delete"), so truncation is unlikely to occur in practice.
-- **Mitigation:** Each `` has `title` attribute on parent button, providing tooltip fallback.
-- **Verdict:** Acceptable — button labels are intentionally short and tooltips are present.
-
-### ✅ Issue #3-INFO: Header text truncation with tooltip
-- **File:** `InstanceCard.tsx:185`
-- **Pattern:** `
{instanceName}
`
-- **Analysis:** Instance names could be long, `truncate` will clip with ellipsis. No `title` attribute on this element — unlike repository text below it.
-- **Recommendation:** Add `title={instanceName}` to the `
` element for tooltip on overflow.
-
-### Passing — no critical overflow issues found.
-
----
-
-## 5. Monitoring / Clusters
-
-**Location:** `frontend/src/features/monitoring/clusters/`
-
-**Layout:**
-- Cluster cards grid: `grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4`
-- Card header: `
` with cluster name
-- Metrics: `grid-cols-2 sm:grid-cols-4 gap-4 mb-3`
-- Resource bars: `overflow-hidden` for proper progress bar clipping
-- Node details: `grid-cols-1 lg:grid-cols-2 gap-3`
-
-**Overflow Check:** ScrollWidth = clientWidth at all tested viewports — no horizontal overflow.
-
-**Responsive:**
-- 1920px: 4 columns of cluster cards
-- 768px: 2 columns
-- 375px: 1 column
-
-### Passing — no issues found.
-
----
-
-## 6. Sidebar Layout
-
-**Location:** `frontend/src/shared/components/layout/SidebarLayout/`
-
-**Layout:**
-- Parent: `min-h-screen flex bg-dark text-primary overflow-hidden`
-- Nav: `flex-1 p-3 space-y-1 overflow-y-auto` — independently scrollable
-- Footer: Fixed at bottom, `p-3 text-xs text-muted`
-
-**Scroll Analysis:**
-- Content area has `overflow-y-auto`, so sidebar nav items scroll independently when they exceed viewport height
-- The footer anchors to the bottom of the sidebar (not the scroll area)
-- At 1080px viewport, sidebar content fits without scrolling
-
-**Potential Concern:** If many nav items are added, the footer will push below the fold and the user must scroll the nav to see it. The `overflow-y-auto` on the `