# 还原 + 性能落地 · 进度 > /loop 自主推进。每波结束本地存档(不 push)。计划:`~/.claude/plans/gleaming-sleeping-ullman.md`。 > 两份审计 checklist:`UI还原对比报告-AirShelf-2026-06-19.md`、`性能审计报告-AirShelf-2026-06-19.md`。 --- ## Wave 0 · 性能根因 — ✅ 完成 (2026-06-19) | # | 项 | 状态 | 改动 / 证据 | |---|---|---|---| | 1 | MySQL 连接复用 | ✅ | `settings/base.py` 加 `CONN_MAX_AGE=300`+`CONN_HEALTH_CHECKS=True`+`connect_timeout=5`(mysql 分支),`development.py` 去重。production 经 `from .base import *` 自动继承。实测注入:`ENGINE=mysql / CONN_MAX_AGE=300 / HEALTH=True / OPTIONS.connect_timeout=5`。**收益最大、最低风险。** | | 2 | 视频轮询不再整页重渲染 | ✅ | `App.tsx` `pollVideosQuiet` 收尾 `setProjectDetail` 改 JSON 脏检查 —— 在途段未变化(多数 5s 轮次)时返回 `prev` 跳过 setState,整棵管线不再每 5 秒空重渲染。纯前端,不碰生成/脚本业务。 | | 3 | 图片懒加载 | ✅ | `ai-tools.tsx` 任务历史网格+列表两处 `` 补 `loading="lazy" decoding="async"`。 | | 4 | 隐藏标签页暂停轮询 | ✅(保守) | `pipeline.tsx` 审核(8s)+ 基础资产在途(4s)两个**纯读**轮询 tick 起始加 `if (document.hidden) return`(回前台自然恢复)。 | ### Wave 0 有意偏离计划(已记录,非偷懒) - **视频 5s 轮询的 hidden-guard:跳过。** 该轮询在本机无 Celery 时**驱动视频生成推进**(前端 poll-video-segment),加 hidden-guard 会让切走标签时生成暂停 —— 触碰「轮询业务逻辑」(loop 规则 3 禁区)且改变行为。Task 2 的脏检查已消除它真正的性能问题(每 5s 整页重渲染),故 hidden-guard 非必要,保守不加。 - **pending-assets「空闲 N 轮后停」:跳过。** 其重启触发不在 effect 依赖里,停了之后用户在该趴新触发的生成将拿不到 loading 占位卡(回归风险)。只加 hidden-guard 已解决「后台标签空跑」的性能点。 - **products/projects 服务端分页:挪到 Wave 1 S7。** 服务端分页必须配可用的分页器(翻页 UI)才不会把「第 21 条以后」彻底锁死;裸上服务端分页而无分页器 = 页面更坏。S7 正是建富分页器,两者合并做才连贯。per-product / ai-tools 的 `pageSize:200` 是有界上限,叠加本波 lazy-img 已可接受,届时一并接服务端分页。 ### Wave 0 验证 - `npx tsc --noEmit` → **exit 0** - `python manage.py check` → **0 issues**;DB 设置实测确认 CONN_MAX_AGE 生效 - 无头浏览器(playwright/chromium,注入 `airshelf_token`)走查 **14 条 authed 路由全部 boot,0 console/page error**(证 App.tsx/pipeline.tsx/ai-tools.tsx 改动无运行时回归) - 登录探针:**用户名字段在、无邮箱字段、点击→/dashboard 726ms**(真·API+水合,无人为延迟) - 截图存档:`_qa_shots/wave0/*.png`(14 路由 + after-login) - 注:Wave 0 改动均为非视觉(设置/轮询/懒加载),pixelmatch 预期 ~0 delta,逐页像素对比留到对应视觉波次(Wave 1/3)做。 --- ## Wave 2 · 后端查询治理 — ✅ 完成(part1 落地 · part2 移交监督) ### part 1 — ✅ 完成 (2026-06-19) | # | 项 | 状态 | 改动 / 证据 | |---|---|---|---| | 索引 | 复合索引(只 makemigrations 看 SQL,**未 apply 远程库**) | ✅ | 5 模型补复合索引:Asset `(team,category,-created_at)`+`(team,asset_type)`、CreditLedger `(team,-created_at)`+`(team,ledger_type,created_at)`(替原 `(team,ledger_type)`)、Project `(team,-updated_at)`、AITask `(team,-created_at)`、Product `(team,-created_at)`。生成迁移 assets/0005·billing/0002·projects/0004·ai/0008·products/0002,`sqlmigrate` 看到的都是干净 `CREATE INDEX`(+billing 一条 DROP 替换)。**远程 MySQL apply 留给用户低峰**(rule 3)。修了一处自摆乌龙:索引误加到 AssetFile,已挪回真正的 Asset 类。 | | 消息 N+1 | type_counts + 项目花费聚合 | ✅ | `ops/views.py` `list()` 的 `type_counts`:原 6 次独立 `count()`(**每请求都跑**)→ 一次 `aggregate(Count(filter=...))` 条件聚合,少 5 个 DB 往返。`ensure_team_notifications` 首跑:5 个项目花费原**逐项目 aggregate**(N+1)→ 一次 `values('project').annotate(Sum)` 预聚合。 | **part 1 验证:** `python manage.py test apps.ops` → **3/3 OK**;全量 39 测试 6 fail+1 error **全部是预存** image_edit/provider 路由失败(stash 我的改动跑 baseline 42c3c04 复现同样失败)→ **零新增回归**(rule 11 ✅);索引迁移在 sqlite 测试库成功 apply(证迁移有效)。 ### part 2 — ⏸️ 主动暂缓(撞 rule 3 禁区,移交用户监督执行) 读码后判定:**两项都落在 rule 3 明令「严禁碰 脚本 agent / 生成 / 轮询的业务逻辑」之内**,且都是对「已验证可用、且我在无人值守 loop 里无法 e2e 验收」的功能动刀 —— 按 rule 14(需用户拍板/需监督验证才停)主动暂缓,给出落地方案供用户监督执行,**不硬改、不敷衍**。Wave 2 的实质收益(索引 + 消息 N+1)已在 part 1 落地。 - **写 action 瘦身 — 暂缓。** projects 的写 action(`adopt-script`/`generate-base-asset`/`generate-triview`/`update|rerun|add|delete-script-segment`/`generate-storyboard`/`submit-video-segment`/`adopt-video-version`/`save-timeline`…)**本身就是管线脚本 agent / 生成业务逻辑**;它们回吐全量 `ProjectSerializer` 是管线据以同步状态的契约,改返回子对象 + 前端 `action()` 改 `liteRefresh`/`refreshProjectDetail` 仅 pipeline 页 = **改管线数据流**,正是 rule 3 禁区。**落地方案**:与 Wave 4 的 pipeline 巨页拆分(同属高风险、计划本就要求「改前与用户确认」)**配对,作一次有监督的管线重构** —— 拆分后逐 action 把返回收窄为受影响子对象,前端同步改部分刷新,然后逐一回归脚本/故事板/生成/导出/拖拽。 - **poll-reviews 挪 Celery — 暂缓。** 这是 `轮询` 端点(rule 3 点名),且改的是**已 e2e 验证拿到真绿盾、用借来的火山 AK/SK(待张业昌换)** 的真人审核子系统。难点:前端 `setReviews(m=>{...m,...map})` 是**合并累加**契约,端点改纯读 DB 必须把「刚翻成 active/failed」的资产也回一次(否则徽章永远停在 processing)—— 需返回「processing + 近 2 分钟刚 resolved」集合,属契约级改动,loop 内无真审核可验。**落地方案**(供监督执行):① 新建 `apps/assets/tasks.py::poll_team_reviews_task(team_id)`(`@app.task`,内部调现成 `poll_team_reviews`,逻辑零改);② 端点 `cache.add` 去重后 `delay` 该任务 + 只读 DB 返回上述集合;③ 起 celery,真生成一张真人基础资产跑通绿盾再合并。 - **charge 通知 bulk_create — 暂缓**(同 part1 记录):仅后台 Celery 跑(非请求阻塞),`create_once` 创建+补字段二态耦合,裸批量风险高/收益小。 **Wave 2 整体 ✅(实质收益 part 1 已落:复合索引 + 消息中心 N+1,零回归;part 2 三项按 rule 3/14 移交用户监督执行,附落地方案)。** --- ## Wave 3 · 逐页还原 P0/P1 — ⏳ 进行中(sub-agent 并行,10 页已落) > rule 4:一页一个 sub-agent 并行,严格只改本页文件(*.tsx + *-page.css),禁碰共享文件(改了在汇报列出我统一处理),禁复用 styles.css 旧暖米类。每批做完我统一 tsc+build+无头走查+截图+commit。 | 批 | 页 | 状态 | 关键还原 + 证据 | commit | |---|---|---|---|---| | 1 | 团队(5.0) | ✅ | 创建账户(role-choice双卡+随机生成+三档额度)/分享凭据(cred-card)/编辑成员/重置密码/月限额 5 弹窗 · 走查 role-choice=2/三额度/预设/ESC 全过 | 0083918 | | 1 | 账户(6.0) | ✅ | 充值扫码弹窗+3筛选条+表格列重排(role-pill/progress) · 走查 充值弹窗/QR/ESC/3筛选条 过 | 0083918 | | 2 | 资产库(4.5) | ✅ | bulk-bar多选+资产详情弹窗+上传Drawer→Modal+缺三视图徽标 · 走查 7卡/详情/上传/ESC 过 | 38f56cb | | 2 | AI工具(5.5) | ✅ | 接入现成 ActorLibrary+gen-card三件套(采用改就地)+平台多选+pl-modal · 走查 模特库/pl-modal 过 | 38f56cb | | 3 | 商品库+详情(6.5) | ✅ | 网格/列表切换+分类多选+素材三态(真实review_status)+采用联动 · 走查 视图切换/list态/chip 过 | da8c6a7 | | 3 | 项目列表(7.0) | ✅ | 进度条+失败pill修err+批量栏+真实镜数 · 走查 4进度条/批量勾选 过 | da8c6a7 | | 3 | 工作台(6.5) | ✅ | KPI接真实(假delta删)+余额真实占比+日期mono+真实镜数 · 走查 4KPI/0报错 过 | da8c6a7 | | 4 | 全局外壳(8.5) | ✅ | 侧栏.user账户菜单(5项+用户名非邮箱) · 走查 菜单5项/ESC 过 | 378b0a3 | | 4 | 设置(8.5) | ✅ | dirty-state引擎(无改动disabled+计数+beforeunload+nav dirty-dot) · 走查 无改动disabled 过 | 378b0a3 | | 4 | 商品创建抽屉(6.5) | ✅ | 多图≤5+pf-grid+拖拽+必填校验+收编草稿 · 走查 multiple/pf-grid 过 | 378b0a3 | **已含 Wave 1 完成项**:全局浮层(2,S3/S4 已 Wave1 做)、工作台 S1 token(Wave1 做)。 **全程零 console error · 每批 tsc 0 / build 0 · sub-agent 全部守「只改本页 + restraint token + 不造假数据」**;真实数据缺口(Project无owner/TeamMember无完成数/非person无review_status初值/dashboard无月度预算/资产「移动到」需api.updateAsset加category)均**降级不造假**并移交监督。 ### Wave 3 收尾 - **消息中心(8.5,P1)— ✅** 详情多动作 actions[](按 type 推断)+ 静音同类(前端 Set)+ 查看日志壳(消费 metadata.log,后端无该字段时显占位不造假)。tsc/build 0。commit 9643f9b。 - **登录页 P0 → 并入 Wave 3.5 已做**(第三方/切换行/lead)。 - **pipeline 视觉(8.5,P1)→ 并入 Wave 4**(rule 3 敏感,与巨页拆分一并谨慎做)。 **Wave 3 整体 ✅:11 页 P0/P1 还原(团队/账户/资产库/AI工具/商品/项目/工作台/外壳/设置/商品创建抽屉/消息),全程 sub-agent 并行、tsc+build+无头走查+截图、0 console error。** --- ## Wave 3.5 · 注册 + 邀请制(新功能)— ✅ 完成 | 层 | 落地 | 证据 | commit | |---|---|---|---| | 后端 | Invitation 模型(code/role/额度/expires/used/status)+ 端点(GET列/POST生成/revoke,权限复用 can_manage_team)+ RegisterSerializer 加 invite_code(有效码→幂等加成员+标used+跳过额度;无码→开新团队) | **apps.accounts 测试 5/5 OK** —— 含「凭码加入同团队/角色额度对/**不重复发额度**/未知码400/普通成员生成403」 | ba855cc | | 前端 scaffolding | types Invitation / api listInvitations·createInvitation·revokeInvitation + register invite_code / route-config AuthMode 双态 / App.tsx URL 切换 | tsc 0 | c0a8582 | | 注册页 | auth-screen 登录+注册双态;注册=团队名/用户名/密码/确认/邀请码(**无邮箱**)+ 价值点+协议+一致性校验+有码/无码提示;登录补第三方+切换行+lead;**登录秒进698ms** | 走查 0 邮箱字段/0「邮箱」文案/切换 URL→/register/0报错 | c0a8582 | | 团队入口 | 「邀请成员」按钮 → 生成码弹窗(角色+月额度)→ cred-card 展示码+邀请链接+复制 | 走查 按钮/弹窗/select/生成钮/ESC 全过 | b82c4d3 | **认证定调贯彻**:全栈无邮箱注册/登录,username 主标识。 > ⚠️ **两组 migration 待用户低峰 apply 到远程 MySQL(rule 3,我不自作主张)**:① Wave 2 复合索引(assets0005/billing0002/projects0004/ai0008/products0002)② Wave 3.5 Invitation(accounts0003)。apply 后「生成邀请码 + 凭码注册」才能真·e2e(逻辑已被后端 5/5 测试在 sqlite 证)。 --- ## Wave 1 · 系统性 CSS/组件 (S1-S9) — ✅ 完成 --- ## Wave 1 · 系统性 CSS/组件 (S1-S9) — ✅ 完成 > 为护住共享 CSS,拆两段做:**part 1 = 浮层/反馈(S3/S4/S9)**,part 2 = token/布局(S1/S2/S6/S7/S8)。 ### part 1 — ✅ 完成 (2026-06-19) | # | 项 | 状态 | 改动 / 证据 | |---|---|---|---| | S4 | 弹窗进场动画 + ESC | ✅ | `overlays.tsx` 新增共享 `useOverlayTransition(open, close)`:挂载→下一帧上 `.show`(触发 `.modal scale(.96→1)` / `.drawer translateX` CSS 过渡)→关闭先撤 `.show` 播退场再卸载;统一接管 ESC。应用到 TeamModal/ConfirmModal/SuccessModal/Drawer。EmptyPanel 补 `.ic-empty` 灰 icon(Inbox)。 | | S3 | Toast 体系归正 | ✅ | `ToastLike`(app-shell)改渲染设计系统 `.toast`(右下、单橙 `.ic-t`、`--shadow-floating`、挂载滑入),**不再随 success/error 变绿/红边**(单一 accent 铁律);icon 按类型 Check/AlertCircle/Info,盒仍橙。`auth-screen` 旧 `.login-toast` → `.toast`。旧 `inline-toast`/`login-toast` 渲染处全部切走(CSS 残留留到 part 2 的 styles.css 治理一并清)。 | | S9 | 表格横线 | ✅ | `account-page.css` 删 `.billing-table tbody td { border-bottom }` + last-child 规则。**逐页对照 V1 account.html 确认**:V1 该表 `td border-bottom:0`、只有 thead 分隔 + 行 hover 底色,React 原先多画的行线是偏离,删后对齐 V1。 | **part 1 验证:** `tsc --noEmit` 0;交互走查(playwright)`auth-toast`:`.toast.show` 出现、文案对、旧 `.login-toast` 计数 0;`team-modal`:`#open-invite` 打开 `.modal-bg.show`(scale 落定=进场动画跑过)、**ESC 成功关闭并卸载**;14 路由回归 boot 全过 0 报错、登录 643ms。截图:`_qa_shots/wave1/*.png`。 ### part 2 — ✅ 完成 (2026-06-19) | # | 项 | 状态 | 改动 / 证据 | |---|---|---|---| | S1 | 双主题清污染 | ✅ | 确认是**真视觉泄漏**:styles.css `:root` 定义 `--orange #E55B26`(暖橙)、`--ink-3 #9C988C`,design-restraint.css 不再声明这些名 → 用 `var(--orange)` 的孤儿类渲染成**错误的橙**。把 Dashboard 区(`.recent-row/.shortcut/.tip` 等,dashboard.tsx 实际在用)从 styles.css 迁入 design-restraint.css,暖米 token → restraint(`--orange→--heat`、`--orange-tint→--heat-12`、`--ink→--accent-black`、`--ink-3→--black-alpha-48`、`JetBrains→--font-mono`);并修 design-restraint.css 自身 `.queue-chip svg` 的 `--ink-3→--black-alpha-56`。**实测**:`.shortcut .ic` color=`rgb(250,93,25)`(=--heat)、bg=`rgba(250,93,25,.12)`、`.tip` color=`rgba(38,38,38,.64)` —— 暖橙泄漏已除。 | | S2 | mono 字号分级 | ✅(基建) | design-restraint.css 加 `.mono-10/.mono-11/.mono-12`(10.5/11/11.5px)工具类。**逐页应用并入 Wave 4 P2 扫**(计划本就如此)。 | | S6 | 批量栏共享 | ⏭️ 挪 Wave 3 | products 的 `.bulk-bar`/`.card-check` **蓝本已在**;其价值是接线到 projects/library = 逐页活,放 Wave 3 重建这俩页时连 CSS 提共享 + 接线一起做才连贯(现在只挪 CSS 无消费者=空动作)。 | | S7 | 富分页器 | ✅(组件) | `pager.tsx` 加**可选** props:`onPageSizeChange`(每页条数点击循环 12/24/48/96)+ 跳页输入(>7 页显示)+ `sticky` 吸底;旧调用方不传则退化为原只读分页器(零破坏)。CSS 加 `.size-cycle/.jump/.sticky`。**接线到 products/library + 服务端分页放 Wave 3**(Wave 0 暂缓项一并)。 | | S8 | hero 字重 | ✅(改 700,证据翻案) | **核 V1 实测**:account.html `.balance-hero .v`/`.balance-sub .v`/`.recharge-card .amt` 与 team.html banner 数字**都是 `font-weight:700`**。V1 设计稿是用户拍板的「视觉标准答案」,虽与 design.md §2.5「700 仅 Ctrl-K」冲突,但**用户明确要还原 V1**→ 据实改 700(account 3 处 + team 2 处)。实测 `.balance-hero .v`=700、`.banner-id .nm`=700。⚠️ 记录此 design.md §2.5 与 V1 设计稿的内部不一致。 | **part 2 验证:** `tsc --noEmit` 0 + `npm run build` 0;computed-style 实测(橙=`rgb(250,93,25)`、hero 字重=700);pixelmatch(修正 Windows file URL 后)account **1.23%** / team **2.13%**(低,字体/真实数据残差,达标);14 路由回归 boot 全过、登录 639ms。截图 `_qa_shots/wave1b/*.png`。 > 注:`core/qa/visual-parity` 的 `compare-all.mjs` 硬编码了原作者 Mac 路径 designRoot、`compare-page.mjs` 不能同时吃 file:// source + token(预存 harness bug,与本次改动无关)——Wave 3 逐页还原把 pixelmatch 当主力工具时一并参数化修掉。 **Wave 1 整体 ✅(S1/S2/S3/S4/S7/S8/S9 落地;S6 挪 Wave 3,S2/S7 逐页应用挪 Wave 3/4)。**