diff --git a/.gitignore b/.gitignore index 54ca230..e2be0b3 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ docker/.env # Local planning docs docs/plan/ docs/plans/ +/.trellis/tasks/ .ace-tool/ diff --git a/.trellis/tasks/08-31-frontend-performance-ux/check.jsonl b/.trellis/tasks/08-31-frontend-performance-ux/check.jsonl deleted file mode 100644 index 255cf3f..0000000 --- a/.trellis/tasks/08-31-frontend-performance-ux/check.jsonl +++ /dev/null @@ -1,3 +0,0 @@ -{"file":".trellis/spec/frontend/hook-guidelines.md","reason":"检查取消和 cleanup"} -{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"检查性能证据和行为回归"} -{"file":".trellis/spec/guides/documentation-language.md","reason":"检查新增任务说明是否遵循中文文档约定"} diff --git a/.trellis/tasks/08-31-frontend-performance-ux/design.md b/.trellis/tasks/08-31-frontend-performance-ux/design.md deleted file mode 100644 index fc00111..0000000 --- a/.trellis/tasks/08-31-frontend-performance-ux/design.md +++ /dev/null @@ -1,40 +0,0 @@ -# 前端请求性能与用户体验:设计 - -## 异步资源模型 - -保持页面本地状态,新增的 hook 只拥有请求生命周期,不拥有领域数据缓存: - -```ts -type AsyncLoader = (signal: AbortSignal) => Promise; -type AsyncResource = { - data: T | null; - loading: boolean; - error: Error | null; - reload: () => void; - cancel: () => void; -}; -``` - -实现可用 AbortController + sequence ref;`api.ts` 负责认证、timeout、错误 -提取和 SSE,页面负责 partial failure、silent refresh 和 mutation invalidation。 -不要把每个页面的 filter/sort 派生结果存成第二份可变状态。 - -## 轮询与并发 - -- Dashboard/ProxyLogs 用单飞锁或 abort previous,保证慢请求不会叠加; -- visibilitychange 时停止定时器,重新可见时最多立即刷新一次; -- SearchModal debounce timer、请求 controller 和关闭/卸载都清理; -- `probeSiteSpeed` 使用固定大小 worker pool、每站超时和结构化结果,不改变 - no-cors 的跨站限制。 - -## 流式渲染 - -ModelTester 累积 delta 和 debug raw 文本到 ref/队列,在 rAF 或 16–50ms 窗口 -内批量提交 React state;完成、停止、错误、abort/unmount 都先 flush 再清理, -确保顺序、重复短 delta、工具调用和 debug 内容不变。 - -## Chunk 与可见性 - -保留已有路由 lazy loading。对 vchart 只做可测量的 below-fold gate 或更细 -入口拆分,不把同一个库复制进多个 vendor chunk;以 network request、LCP/TTI -近似指标和图表出现后的布局稳定性验收。 diff --git a/.trellis/tasks/08-31-frontend-performance-ux/implement.jsonl b/.trellis/tasks/08-31-frontend-performance-ux/implement.jsonl deleted file mode 100644 index 7bda8da..0000000 --- a/.trellis/tasks/08-31-frontend-performance-ux/implement.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"file":".trellis/spec/frontend/hook-guidelines.md","reason":"异步取消、timer 和 stale guard"} -{"file":".trellis/spec/frontend/state-management.md","reason":"保持页面本地 server state"} -{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"性能/UX 回归和测试门"} -{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"API/SSE/跨域测速边界"} -{"file":".trellis/spec/guides/documentation-language.md","reason":"保持 Trellis 实施文档和清单使用简体中文"} diff --git a/.trellis/tasks/08-31-frontend-performance-ux/implement.md b/.trellis/tasks/08-31-frontend-performance-ux/implement.md deleted file mode 100644 index 4eb05bd..0000000 --- a/.trellis/tasks/08-31-frontend-performance-ux/implement.md +++ /dev/null @@ -1,25 +0,0 @@ -# 前端请求性能与用户体验:执行清单 - -1. 记录 baseline(build chunk、页面请求时间线、自动刷新并发、Search stale、 - ModelTester render/flush 计数),建立可重复的 fake API/SSE fixtures。 -2. 实现并测试 `useAsyncResource`/等价 helper,先迁移 SearchModal 和一个简单 - 页面,再迁移 Dashboard、ProxyLogs。 -3. 修复 Dashboard visibility/single-flight、ProxyLogs 2 秒刷新锁、SearchModal - debounce abort、Models hydrate timer cleanup。 -4. 抽取 Dashboard `probeSiteSpeed`,加入并发池、超时和逐站结果;保留明确的 - cross-origin exception 注释和 drift allowlist。 -5. 为 ModelTester 流式更新加批处理并做内容等价测试。 -6. 评估 ProxyLogs/Accounts progressive list 与图表可见性门控,只有有数据才 - 实施;补移动/键盘/空态/错误回归。 -7. 生成前后性能报告并运行: - -```bash -npm test -- src/web/components/search-modal.results.test.tsx src/web/pages/dashboard.*.test.tsx src/web/pages/ProxyLogs.server-driven.test.tsx -npm run typecheck:web -npm run build:web -npm run repo:drift-check -git diff --check -``` - -停止条件:内容顺序或协议字段变化、后台轮询继续堆积、跨域测速丢失、或无法 -证明性能收益时,保留基线并停止该优化,不扩大重构。 diff --git a/.trellis/tasks/08-31-frontend-performance-ux/prd.md b/.trellis/tasks/08-31-frontend-performance-ux/prd.md deleted file mode 100644 index 53ce588..0000000 --- a/.trellis/tasks/08-31-frontend-performance-ux/prd.md +++ /dev/null @@ -1,60 +0,0 @@ -# 前端请求性能与用户体验 - -## 目标 - -在不改变页面能力和 API 契约的前提下,统一可取消/防陈旧的请求生命周期, -降低重复请求和无谓渲染,改善大列表、图表、搜索、轮询和模型测试器的响应, -并让加载、空态、错误和取消反馈在页面间一致。 - -## 已确认事实 - -- 规划阶段的 `npm run build:web` 采样为 `2613 modules transformed`、约 `5.35s`; - `vchart-vendor` 为 `2,154.02 kB`(gzip `586.81 kB`),是唯一明显超大的 chunk。 - 最终交付复核为 `2614 modules`、CSS `136.83 kB`,详见项目审计;旧的 2,548 - modules / 5.30s 数字只作为历史材料。 -- Dashboard 首屏有三组数据请求,可见性轮询没有 in-flight single-flight; - ProxyLogs 每 2 秒刷新但没有请求并发锁;SearchModal debounce 没有 abort/ - sequence;Models hydrate timer 未完整清理。 -- ModelTester 对每个 SSE chunk 更新消息和 debug 状态,可能造成整树高频渲染; - Dashboard 对所有站点测速使用无上限 `Promise.all`。 -- TokenRoutes 已有 progressive chunk + IntersectionObserver,可作为评估样例; - 规范要求继续使用 React state + `api.ts`,不新增全局 server-state 库。 -- 当前工作树的 `useAsyncResource`、`siteSpeedProbe` 和 `streamBatcher` 是未提交 - 候选/WIP;在 focused tests、typecheck、build 和 review 通过前,不视为已完成 - 的产品能力。 - -## 需求 - -1. 提供窄职责、类型安全的可取消异步 hook/helper,支持 stale guard、silent - refresh、AbortError/timeout 区分和 unmount cleanup;迁移至少四个页面路径。 -2. 修复 SearchModal、Dashboard、ProxyLogs、Models 的竞态、timer 和轮询行为; - 页面切后台时停止高频轮询,回到前台只做一次刷新。 -3. 将 Dashboard 跨域测速抽成共享 `probeSiteSpeed`/等价 helper,限制并发、 - 超时和逐站结果映射,保留 no-cors 特殊能力并记录架构例外。 -4. 对 ModelTester 流式 delta/debug 更新做 16–50ms 批处理,停止/abort 时 - flush,保证最终文本和 debug 字节序列不变。 -5. 只在测量证明必要时复用 progressive list 或图表可见性门控,避免首屏提前 - 下载 vchart vendor;保证图表出现时无布局跳动和功能缺失。 -6. 统一选定页面的 loading/empty/error/cancel feedback 和翻译,不把错误吞掉。 -7. 输出前后 chunk、请求在途数、stale response 和关键交互测量报告。 - -## 验收标准 - -- [ ] async helper 有 abort、stale、silent refresh、unmount 和错误分类测试; - 至少四个真实页面使用它。 -- [ ] SearchModal 快速 q1→q2 只显示 q2;Dashboard/ProxyLogs 自动刷新在慢 - 请求下最多一个在途请求;停止/卸载后没有残留请求或 timer。 -- [ ] Dashboard 测速并发上限可测试(目标不超过 4),逐站失败可见,跨域能力 - 不丢失。 -- [ ] ModelTester 内容完全一致且渲染频率/长任务指标改善或有证据说明瓶颈 - 已转移;首字响应不出现可感知回退。 -- [ ] 首屏/路由 chunk 不回归,below-fold 图表不会无条件提前下载超大 vendor; - 图表可见后仍正常渲染。 -- [ ] 桌面、移动、键盘、翻译、加载/空态/错误/取消行为有回归测试。 -- [ ] web typecheck、focused/full tests、drift check、build 和 diff check 通过。 - -## 范围外 - -- 引入 React Query/SWR/Zustand 等全局数据层; -- 改变服务器 API 响应、代理协议、分页语义或删除功能; -- 在没有测量的情况下对所有列表做窗口化或重写所有页面。 diff --git a/.trellis/tasks/08-31-frontend-performance-ux/task.json b/.trellis/tasks/08-31-frontend-performance-ux/task.json deleted file mode 100644 index 5b52b03..0000000 --- a/.trellis/tasks/08-31-frontend-performance-ux/task.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id": "frontend-performance-ux", - "name": "frontend-performance-ux", - "title": "前端请求性能与用户体验", - "description": "统一可取消异步状态,优化请求与渲染性能和跨页面体验,同时保持现有行为。", - "status": "planning", - "dev_type": null, - "scope": null, - "package": null, - "priority": "P1", - "creator": "cita-777", - "assignee": "cita-777", - "createdAt": "2026-08-31", - "completedAt": null, - "branch": null, - "base_branch": "main", - "worktree_path": null, - "commit": null, - "pr_url": null, - "subtasks": [], - "children": [], - "parent": "08-31-project-wide-simplification", - "relatedFiles": [], - "notes": "", - "meta": {} -} diff --git a/.trellis/tasks/08-31-project-wide-simplification/audit.md b/.trellis/tasks/08-31-project-wide-simplification/audit.md deleted file mode 100644 index 538791c..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/audit.md +++ /dev/null @@ -1,120 +0,0 @@ -# 全项目简化基线审计(2026-08-31) - -## 执行环境与命令 - -- Node `v22.23.1`、npm `10.9.8`。 -- `npm run typecheck:web`:通过。 -- `npm run build:web`:当前工作树最终复核通过,`2614 modules transformed`,约 `4.77s`。 -- web CSS:`136.83 kB`(gzip `22.01 kB`);`vchart-vendor`:`2,154.02 kB` - (gzip `586.81 kB`)。 -- `npm run repo:drift-check`:当前工作树为 `0 violations / 0 tracked debt`;同一 - checker 只读扫描 `HEAD` 为 `5 violations / 0 tracked debt`。这里的两种口径必须 - 分开记录,不能把当前未提交 WIP 的结果写成历史基线。 -- 本审计只读,不修改产品源码;当前工作树的 `.agents/`、`.codex/`、 - `.trellis/`、`graphify-out/`、`output/` 等无关文件保持原状。 - -## 前端结构与样式 - -| 观察 | 证据 | 影响 | -| --- | --- | --- | -| 路由已经 lazy load | `src/web/App.tsx:22-39` | 不应重复引入第二套路由/页面技术栈 | -| 全局 CSS token/class 是主样式来源 | `src/web/index.css:1-70`、6,908 行、62 个 CSS variables | 适合继续作为 Metapi 视觉 owner | -| Tailwind 入口存在但 utility 实际使用很少 | `vite.config.ts:1-18`、`index.css:1`;生产扫描仅命中极少工具类 | 存在冗余构建入口,应在阶段 A 以 build 证据决定清理 | -| 共享 modal/drawer/select 已存在 | `components/CenteredModal.tsx`、`MobileDrawer.tsx`、`ModernSelect.tsx` | 应保留 API 做 facade,而非一次性重写 | -| 约 430 个原生 button、无统一 Button | 高频集中于 Accounts/Sites/Settings/OAuth/Tokens | 重复 disabled/spinner/aria 组合,易产生视觉和行为漂移 | -| inline style 热点 | Settings 192、ProxyLogs 185、Sites 134、Models 119、Accounts 109、ModelTester 108 | 先抽高频布局/反馈 primitive,避免机械全量替换 | -| 超大页面 | ProxyLogs 3,832、Accounts 3,499、ModelTester 3,401、Settings 2,647、Sites 2,426 | 状态、编排、渲染混在单文件,适合按领域拆分 | - -## UI/无障碍重复 - -- `ChangeKeyModal`、`SearchModal`、settings 多个 modal、下游/OAuth drawer 各自 - 复制 portal/backdrop/动画/滚动锁;部分缺 `role`、`aria`、focus return。 -- `TokenRoutes.tsx` 有 `document.createElement + innerHTML` imperative dialog; - `window.confirm` 分散在 ImportExport、OAuthManagement、Settings、TokenRoutes。 -- `ModernSelect` 的 `options/onChange` 和组件类型被测试直接断言,必须先做 - facade,再考虑内部 Radix Select。 -- Toast 已有集中 provider,但缺 `aria-live` 和完整 timer cleanup。 - -## 请求与性能热点 - -- `Dashboard.tsx:250-319` 首屏三组请求;可见性轮询没有在途锁。 -- `ProxyLogs.tsx:975-1020,1084-1090` 有 sequence guard 但 2 秒刷新可能堆积 - 慢请求。 -- `SearchModal.tsx:78-121` debounce 没有 abort/sequence,旧结果可能覆盖新查询。 -- `Models.tsx:189-240` 的延迟 metadata hydrate timer 需要卸载清理。 -- `ModelTester.tsx:1740+` 每个 SSE chunk 都 setState,适合批处理但必须验证 - 文本顺序、工具调用和 debug 字节不变。 -- `Dashboard.tsx:1347-1443` 对每个站点用 `Promise.all` 做跨域 `no-cors` 测速; - 该能力不能伪装成认证 API,应抽成有限并发 helper 并保留明确例外。 -- `vchart-vendor` 2,154.02 kB(gzip 586.81 kB)是唯一超过 500 kB 的 chunk; - 先做 below-fold 可见性测量,再决定是否拆入口。 - -## 服务端重复与边界债务 - -- `src/server/routes/api/accounts.ts:721-830,966-1055` 两套验证诊断同构, - 需统一 shield、user-id、Bearer/Cookie、endpoint pool 和网络错误语义。 -- `src/server/services/platforms/oneApi.ts:57-98` 与 `veloera.ts:34-75` 重复 - check-in、balance、models/context cache,差异可配置化。 -- `geminiSurface.ts:247-299`、`routes/proxy/completions.ts:372-425`、 - `embeddings.ts:217-270`、`images.ts:461-514`、`search.ts:217-268` 重复 - proxy log 编排,应统一结构化 owner 并保留 path/usageSource/billing 默认值。 -- `HEAD` 的 drift report 有 5 条违规:4 条 proxy-core → routes/proxy import、 - 1 条 Accounts → Tokens page import;当前工作树 checker 已暂时消除这些路径, - 但只有迁移真实完成并经 review 后才可把它当作稳定结论。 - -## 已确定方向与限制 - -- 采用 Iris-like 的 Radix 原语 + 项目封装层,不引入 Arco/Ant Design 全套视觉 - 组件;页面和领域组件不直接 import 第三方 UI。 -- 保留 Metapi 现有靛蓝/灰色视觉 token 和业务组件,不复制 Iris 暖色主题。 -- 不新增 React Query/SWR/Zustand 全局 server-state 层,不改数据库 schema、 - 代理协议、公开 API 或用户能力。 -- 跨域测速、登录 bootstrap 等 raw fetch 只能作为有 owner、有注释的例外,不能 - 变成新页面的默认模式。 - -## 审计局限 - -- 未在本机运行真实浏览器性能 trace 或生产网络;chunk/request 指标是本地构建 - 和静态/测试夹具基线。 -- live MySQL/PostgreSQL 服务未作为本任务前置条件;服务端阶段不改 schema。 -- 现有工作树的用户 dirty/untracked 内容不属于本审计,不能据此推断其意图。 - -## 当前工作树候选核验与 owner 映射(2026-08-31) - -下表描述当前 dirty worktree 中已出现的候选切片,不表示已提交、已发布或父/子 -任务全部完成;未提交的产品代码仍由各自任务负责,必须在任务级 quality gate -完成后才能升级为交付结论。这里仍然坚持有明确 owner 和回归证据,未把“文件变少” -当成协议统一的充分理由: - -| 交付物 | 唯一 owner | 证据与边界 | -| --- | --- | --- | -| Dialog/Drawer/Button/feedback | `src/web/components/ui/**` | role/aria、focus return、Escape/backdrop、body-lock 多实例和 loading 行为测试;`CenteredModal`/`MobileDrawer` 保留 facade | -| ModernSelect | `src/web/components/ui/Select.tsx` | 保留 `options/value/onChange/placeholder` 与 listbox 键盘语义;仍是项目兼容 facade,尚未声称迁移 Radix Select | -| 异步请求生命周期 | `src/web/components/useAsyncResource.ts` + `src/web/api.ts` | abort、stale、silent refresh、single-flight、timeout/body/SSE cleanup 测试;页面继续拥有领域 state | -| Dashboard 测速 | `src/web/pages/helpers/siteSpeedProbe.ts` | 固定并发上限 4、逐站 timeout/result、`no-cors` browser-only 例外和可见按钮状态测试 | -| ModelTester 流式更新 | `src/web/pages/model-tester/streamBatcher.ts` | 24ms 批处理;done/stop/abort/unmount flush,cumulative delta 与 raw event 顺序等价测试 | -| Tokens 页面边界 | `src/web/pages/tokens/TokensPanel.tsx` | `Tokens.tsx` 仅 legacy redirect/兼容导出;解除 top-level page-to-page import | -| 账号验证诊断 | `src/server/services/accountVerificationDiagnostics.ts` | Bearer/Cookie、shield、user-id、endpoint/timeout/no-response 结构化诊断与 route envelope 回归 | -| proxy-core helper | `src/server/services/downstreamPolicyService.ts`、`multipart.ts` | proxy-core 不再依赖 `routes/proxy/**`;旧路径仅 re-export facade,并有 architecture test | - -候选切片此前记录的门禁结果(仅作为当前工作树证据,需在最终任务检查时重跑): - -- Web:157 个测试文件、539 个测试通过,2 个跳过; -- Server:301 个测试文件、2,155 个测试通过,6 个跳过; -- 全仓库 `npm test`:480 个测试文件通过、2,777 个测试通过,1 个文件/8 个测试跳过; -- `npm run typecheck`、`npm run build`、`npm run repo:drift-check`、 - `git diff --check` 曾通过;当前 checker 的 drift 为 `0 violations / 0 tracked debt`; -- `better-sqlite3` 首次受 Node ABI 不匹配影响,执行 `npm rebuild better-sqlite3` - 后再运行 server suite;该操作只改 `node_modules`,没有改仓库产物。 - -仍明确保留的后续项和未决风险: - -1. `vchart-vendor` 约 2.15 MB(gzip 约 587 kB)仍是最大 chunk。当前没有真实 - 浏览器 below-fold/network trace,因此不实施未经测量的图表拆分;后续任务须 - 提供首屏请求、LCP/TTI 和布局稳定性证据。 -2. OneApi/Veloera 全面继承统一与全部 proxy-log owner 迁移暂缓。header、quota - divisor、check-in 文案、model error propagation、usage/billing 默认值和 - await 时序存在真实差异;继续合并前必须先补参数化协议回归。 -3. `testChatStream()` 保留 raw `fetch()`,因为它有意返回原始 `Response` 并由 - 调用方拥有 body;跨域测速和登录 bootstrap 同样是已记录的 browser-only/API - boundary 例外,不应被全面禁止规则破坏。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/check.jsonl b/.trellis/tasks/08-31-project-wide-simplification/check.jsonl deleted file mode 100644 index 9099c1f..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/check.jsonl +++ /dev/null @@ -1,9 +0,0 @@ -{"file":".trellis/spec/guides/documentation-language.md","reason":"检查任务验收和实现变更中的 Trellis 文档是否遵循简体中文默认语言,以及机器可读字段是否保持不变。"} -{"file": ".trellis/spec/frontend/quality-guidelines.md", "reason": "检查前端验证门和回归风险"} -{"file": ".trellis/spec/backend/quality-guidelines.md", "reason": "检查服务端边界和测试要求"} -{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "检查跨层回归与契约漂移"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/audit.md", "reason": "核对实现是否回应已记录的热点和边界"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/repository-baseline.md", "reason": "核对构建、drift、Git dirty 状态和数据库范围"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/frontend-architecture.md", "reason": "核对页面、请求、样式和候选性能改动边界"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/backend-architecture.md", "reason": "核对服务端继承关系、proxy log owner 和跨层债务"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/trellis-runtime.md", "reason": "核对 workflow、session pointer、JSONL 和 hook 运行时契约"} diff --git a/.trellis/tasks/08-31-project-wide-simplification/design.md b/.trellis/tasks/08-31-project-wide-simplification/design.md deleted file mode 100644 index 6d7ab79..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/design.md +++ /dev/null @@ -1,173 +0,0 @@ -# 全项目简化与前端一致性:技术设计 - -## 1. 设计目标与非目标 - -本任务的目标是把已经存在的重复行为收敛到明确的 owner,降低页面和服务 -文件的编排复杂度,并让后续新增页面只能沿用同一套组件、样式和请求边界。 -所有重构都必须保持现有路由、协议字段、数据库语义、权限语义和用户可见能力。 - -本任务不做全量 UI 换肤,不把业务页面改造成第三方模板,不引入全局状态库来 -替代页面状态,也不借“简化”之名删除功能或跳过现有兼容测试。 - -## 2. 目标架构 - -### 2.1 Web 组件边界(Iris 参考模式) - -```text -路由页 pages/** - ↓ 只编排数据、URL intent 和领域状态 -领域组件 pages//** - ↓ 只使用项目自己的语义 props -项目 UI 封装 components/ui/** - ↓ 唯一允许直接引用第三方 UI 原语 -Radix primitives + Metapi CSS tokens/classes -``` - -- Radix 只负责 Dialog、Select、Tabs、Popover 等交互、焦点和无障碍原语; - 不让路由页直接 import `@radix-ui/*`。 -- `components/ui/**` 是稳定的项目 API。先保留现有 - `CenteredModal`、`MobileDrawer`、`ModernSelect` 的导出和 props 兼容,再 - 在内部逐步委托给 Radix,避免一次改变所有页面和测试树。 -- Metapi 继续使用现有靛蓝/灰色 CSS 变量和语义 class。Iris 的 Tailwind - 是参考项目的实现细节,不要求 Metapi 复制其暖色 token 或整体迁移到 - Tailwind。 -- `Button`/`AsyncButton`、`Dialog`/`Drawer`、`Select`、`FormField`、 - `AsyncState`、`EmptyState`、`ErrorState` 等基础组件由项目封装层统一 - 拥有。业务卡片、路由编辑器、模型测试器和图表继续由领域模块拥有。 - -### 2.2 样式入口 - -当前生产代码只有极少量真正的 Tailwind utility 使用,大部分页面依赖 -`src/web/index.css` 的 token/class 和 inline style。第一阶段先用静态搜索和 -构建产物确认这一事实;若确认没有必须保留的 utility,移除 Tailwind Vite -插件、CSS import 和依赖,并把现有 CSS token/class 明确记录为唯一样式入口。 -若搜索发现实际依赖,则保留 Tailwind,但必须将其设为唯一 utility 入口并 -禁止新增另一种 CSS 运行时。无论结果如何,页面都不能各自选择技术栈。 - -### 2.3 异步请求和页面状态 - -仓库不引入 React Query/SWR 作为全局 server-state 层(现有规范明确页面状态 -由 React state + `api.ts` 管理)。新增一个窄职责的可取消异步 hook,例如: - -```ts -type AsyncLoader = (signal: AbortSignal) => Promise; - -type AsyncResource = { - data: T | null; - loading: boolean; - error: Error | null; - reload: () => void; - cancel: () => void; -}; -``` - -具体命名以实现前搜索结果为准,但必须满足: - -- 每次 target/filter 变化都会取消或使旧请求失效; -- `AbortError`、超时和真实失败在 UI 中可区分; -- silent refresh 不清空已经可用的数据; -- 页面仍然决定自己的 partial-failure 和 mutation reload 语义; -- 不把派生过滤结果再复制成第二份可变 server state。 - -`src/web/api.ts` 继续是认证、超时、错误提取和 SSE 的唯一边界。跨域 no-cors -的站点测速是明确的特殊能力,必须由单独的 `probeSiteSpeed` helper 统一 -并发、超时和结果映射,而不是把它伪装成普通管理 API。 - -### 2.4 服务端所有权 - -- 账号验证诊断下沉到中性 service(建议 - `src/server/services/accountVerificationDiagnostics.ts`),route 只负责 - base URL/endpoint pool 选择和 HTTP 响应适配。 -- 代理日志写入通过结构化 `recordProxyLog` owner 统一,保留不同 endpoint - 的 path、usageSource、billing/token 默认值和 warning scope。 -- OneApi/Veloera 目前都直接继承 `BasePlatformAdapter`,不是 - `StandardApiProviderAdapterBase` 的子类。若要收敛共同流程,先抽取中性策略或 - helper,并用 header、quota divisor、models fallback、check-in capability 的 - 参数化测试证明兼容;不得凭空改继承树。 -- `proxy-core` 不再新增对 `routes/proxy` 的依赖。当前 `HEAD` 仍有 5 条违规:4 条 - proxy-core → routes/proxy,1 条 Accounts → Tokens page;当前工作树有对应的 - 中性 service/facade 候选,但只有通过完整检查后才能删除 drift allowlist。 -- Accounts 的 `TokensPanel` 迁移到领域子目录,解除 top-level page 互相 import 的 - 一项边界债务;是否稳定纳入交付仍以 architecture test 和 drift 结果为准。 - -## 3. 分阶段交付 - -### 阶段 A:UI 基础与边界守卫 - -1. 引入最小 Radix 依赖并建立 `components/ui` wrapper;保留旧组件 API。 -2. 统一 Dialog/Drawer 的 role、aria、焦点返回、body-scroll 引用计数、 - Escape/backdrop 策略和动画退出行为。 -3. 将复制的 settings modal、Search/ChangeKey modal、下游/OAuth drawer、 - `window.confirm`/imperative dialog 逐步改为 wrapper;优先改高风险和 - 低业务耦合路径。 -4. 以 facade 方式改造 `ModernSelect`,先保持 `options/onChange` 和测试中 - 的组件类型,再逐步替换内部实现。 -5. 增加 `Button`/`AsyncButton` 和统一反馈 primitive,按页面增量迁移。 -6. 扩展 drift check:第三方 UI 直引、认证页面 raw fetch、超大页面 ratchet; - 允许项必须有注释和 tracked-debt 记录。 - -### 阶段 B:请求、性能与 UX - -1. 先迁移 SearchModal、Dashboard、ProxyLogs 三个高收益路径到可取消/单飞 - 请求模式;再迁移至少一个普通管理页验证可复用性。 -2. 修复 Dashboard 可见性轮询、ProxyLogs 2 秒自动刷新、SearchModal debounce - 竞态和 Models hydrate timer 清理。 -3. 把 Dashboard 站点测速改为有上限的并发池,保留跨域 no-cors 能力和逐站 - 失败反馈。 -4. 对 ModelTester 流式 delta/debug 更新做 16–50ms 的 rAF/批处理,停止或 - abort 时强制 flush,保持字节级内容一致。 -5. 复用 TokenRoutes 的 progressive chunk/IntersectionObserver 模式评估 - ProxyLogs/Accounts 大列表;只有测量证明有益时才引入窗口化。 -6. 对 below-fold 图表增加可见性门控或更细粒度 chunk,目标是首屏不提前 - 下载 2.15MB vchart vendor;图表出现时仍必须正常挂载。 -7. 把高频重复 inline style、按钮和 empty-state markup 迁移到 wrapper/class, - 不进行机械式全量替换;每次替换都保留视觉和响应式测试。 - -### 阶段 C:服务端重复与跨层债务 - -1. 合并 accounts 两套验证诊断并保留 endpoint pool、网络错误和失败词汇 - 语义。 -2. 核对 `OneApiAdapter`/`VeloeraAdapter` 的真实继承和差异;先以中性策略与 - 参数化 adapter 测试验证,再决定是否收敛共同流程,不强行改为 - `StandardApiProviderAdapterBase` 子类。 -3. 统一 completions/embeddings/images/search/Gemini 的 proxy log 写入。 -4. 迁移 `downstreamPolicy`、multipart helper 到中性 owner,验证并删除对应的 - proxy-core drift 违规;抽取 TokensPanel 消除页面互引。 -5. 对所有跨层改动运行 route/service/transformer architecture tests,确认 - 不改变协议转换和数据库行为。 - -## 4. 兼容性与回滚 - -- wrapper 优先保持现有导出名、props 和 CSS class;旧组件可作为 facade, - 以便逐页回滚。 -- 每个阶段以独立 commit/子任务交付,出现视觉、焦点或协议回归时只回滚 - 对应 wrapper/迁移 commit,不回滚用户已有的无关工作。 -- 不做数据库迁移;若发现必须改 schema,暂停本任务并另建 schema 需求。 -- 不把 `window.confirm` 的同步调用直接替换成未设计好的异步状态;先保留 - 过渡 helper 或逐个重写调用方及测试。 -- 跨域测速和登录 bootstrap 的 raw fetch 例外必须在架构报告中标注原因、 - owner 和后续迁移状态,不能用“全面禁止”破坏能力。 - -## 5. 可观测基线与验收数据 - -当前已验证: - -- `npm run typecheck:web`:通过(Node v22.23.1 环境); -- 规划阶段 `npm run build:web` 采样为 `2613 modules transformed`,约 `5.35s`; - 这是 Node `v22.23.1` 的本地基线,不是发布环境证明。最终复核见任务审计和 - `docs/frontend-architecture.md`。 -- 规划阶段 web CSS:`136.78 kB`(gzip `22.00 kB`); -- 最大 chunk:`vchart-vendor` `2,154.02 kB`(gzip `586.81 kB`); -- 其他较大路由 chunk:TokenRoutes `176.61 kB`、Settings `105.96 kB`、 - ModelTester `88.98 kB`、DownstreamKeys `65.48 kB`、Accounts `60.73 kB`、 - Sites `59.67 kB`、ProxyLogs `58.56 kB`; -- 当前工作树运行 `npm run repo:drift-check`:`0 violations / 0 tracked debt`; - 同一 checker 只读扫描 `HEAD` 时为 `5 violations / 0 tracked debt`,不能把两种 - 口径混写成“历史 debt 已清零”。 -- inline `style={{` 热点(文件级近似):Settings 192、ProxyLogs 185、 - Sites 134、Models 119、Accounts 109、ModelTester 108; -- 页面行数热点:ProxyLogs `3,832`、Accounts `3,499`、ModelTester `3,401`、 - Settings `2,647`、Sites `2,426`、TokenRoutes `2,041`、Dashboard `1,710`。 - -验收必须同时记录构建 chunk、请求在途数、stale response、关键交互和测试 -结果;单纯“编译成功”不视为性能或 UX 完成。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/implement.jsonl b/.trellis/tasks/08-31-project-wide-simplification/implement.jsonl deleted file mode 100644 index 1e2e993..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/implement.jsonl +++ /dev/null @@ -1,16 +0,0 @@ -{"file":".trellis/spec/guides/documentation-language.md","reason":"确保任务实现阶段新增或修改的 Trellis 文档默认使用简体中文,并保留命令、路径和机器字段的规范拼写。"} -{"file": ".trellis/spec/guides/code-reuse-thinking-guide.md", "reason": "约束抽象边界和避免重复实现"} -{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "约束跨层契约和数据流审计"} -{"file": ".trellis/spec/frontend/component-guidelines.md", "reason": "Iris-like wrapper 与无障碍组件规范"} -{"file": ".trellis/spec/frontend/hook-guidelines.md", "reason": "异步 hook、cleanup 和 stale guard 规范"} -{"file": ".trellis/spec/frontend/state-management.md", "reason": "页面状态与 API 边界规范"} -{"file": ".trellis/spec/frontend/quality-guidelines.md", "reason": "前端测试、漂移和可访问性门"} -{"file": ".trellis/spec/backend/directory-structure.md", "reason": "服务端所有权边界"} -{"file": ".trellis/spec/backend/error-handling.md", "reason": "错误、重试和 best-effort 语义"} -{"file": ".trellis/spec/backend/quality-guidelines.md", "reason": "服务端质量与架构守卫"} -{"file": "docs/plans/2026-03-23-single-source-consolidation-mega-plan.md", "reason": "避免重复已交付的历史整合"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/audit.md", "reason": "当前仓库证据、基线指标和审计局限"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/repository-baseline.md", "reason": "仓库规模、构建、Git、数据库和验收基线"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/frontend-architecture.md", "reason": "前端页面边界、请求例外、性能热点和候选 helper"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/backend-architecture.md", "reason": "服务端 adapter、proxy-core、日志 owner 和 drift 事实"} -{"file": ".trellis/tasks/08-31-project-wide-simplification/research/trellis-runtime.md", "reason": "Trellis workflow、任务生命周期、JSONL、workspace 和 hook 契约"} diff --git a/.trellis/tasks/08-31-project-wide-simplification/implement.md b/.trellis/tasks/08-31-project-wide-simplification/implement.md deleted file mode 100644 index 6864548..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/implement.md +++ /dev/null @@ -1,124 +0,0 @@ -# 全项目简化与前端一致性:执行计划 - -## 执行前检查 - -- [x] 已读取根 `AGENTS.md`、frontend/backend Trellis specs、代码复用和跨层 - 思考指南。 -- [x] 已检索历史 single-source consolidation,避免重复已交付工作。 -- [x] 已建立 web typecheck、web build、drift check 和 chunk 基线。 -- [x] 已确认用户选择 Iris-like:Radix 原语 + 项目封装层。 -- [ ] 在 `task.py start` 前完成最终规划评审;未获评审批准不得改产品代码。 - -## 子任务顺序 - -### 1. `08-31-frontend-ui-foundation` - -目标:建立唯一的 `components/ui` 边界,修复共享弹窗/抽屉/选择器的可访问 -性和生命周期问题,并建立防止第三方 UI 直引的 architecture guard。 - -建议顺序: - -1. 确认 Radix 最小依赖集合,更新 npm/pnpm lockfile 并验证 React 18 兼容; -2. 新增 Dialog/Drawer wrapper,保留 `CenteredModal`/`MobileDrawer` facade; -3. 为多个 portal 的 body lock、focus return、Escape/backdrop 写回归测试; -4. 迁移 SearchModal、ChangeKeyModal、settings modal、下游/OAuth drawer; -5. 迁移 `window.confirm` 和 TokenRoutes imperative dialog,覆盖 remember/ - dismiss 语义; -6. 以 facade 改造 `ModernSelect`,再增量加入 `Button`/`AsyncButton` 与反馈 - primitive; -7. 清理已确认未使用的 Tailwind 入口(或记录保留理由),建立 wrapper import - architecture test 和 drift rule; -8. 运行组件及页面焦点、移动布局、类型和构建测试。 - -风险文件:`src/web/components/CenteredModal.tsx`、`MobileDrawer.tsx`、 -`ModernSelect.tsx`、`Toast.tsx`、`src/web/App.tsx`、 -`src/web/pages/TokenRoutes.tsx`、settings/downstream/oauth 子目录。 - -### 2. `08-31-frontend-performance-ux` - -依赖阶段 1 的 wrapper/API 边界,但不要求等待所有页面 UI 迁移完成。 - -建议顺序: - -1. 新增并测试窄职责 `useAsyncResource`/`useCancellableLoad`(最终命名按 - 代码搜索结果决定),覆盖 abort、stale、silent refresh、unmount; -2. 迁移 SearchModal、Dashboard、ProxyLogs 和一个普通管理页; -3. 抽取 `probeSiteSpeed` 并将 Dashboard 站点测速限制并发、统一 timeout/result; -4. 修复 Dashboard/ProxyLogs 自动刷新 single-flight 和 visibility 行为; -5. 修复 Models hydrate timer,给 ModelTester 流式更新加批处理并验证内容 - 完整性; -6. 仅在测量证明必要时复用 TokenRoutes progressive list 或图表可见性门控; -7. 迁移选定页面的共享 loading/error/empty/inline style,并做桌面/移动 - 回归; -8. 生成前后性能报告(chunk、请求并发、render/long-task 近似指标)。 - -风险文件:`src/web/api.ts`、`SearchModal.tsx`、`Dashboard.tsx`、 -`ProxyLogs.tsx`、`ModelTester.tsx`、`Models.tsx`、相关页面测试。 - -### 3. `08-31-server-boundary-simplification` - -依赖阶段 1 的架构守卫定义,但可独立实现和验证。 - -建议顺序: - -1. 抽取 `accountVerificationDiagnostics`,以参数化测试锁定 shield、user-id、 - bearer/cookie、endpoint pool、网络超时和失败 reason; -2. 先核对 `OneApiAdapter`/`VeloeraAdapter` 都直接继承 `BasePlatformAdapter` 的 - 事实;在中性策略 owner 和参数化测试证明兼容前,不改成 - `StandardApiProviderAdapterBase` 子类。显式保留 quota divisor、header、 - models fallback、check-in capability 和文案差异; -3. 将各 proxy path 的 `logProxy` 改为结构化 `recordProxyLog` owner; -4. 迁移 downstream policy、multipart helper 和 Accounts TokensPanel,删除 - drift allowlist 中已解决的 tracked debt; -5. 运行 API/proxy/service/transformer architecture tests、server typecheck、 - drift check 和相关构建。 - -风险文件:`src/server/routes/api/accounts.ts`、 -`src/server/services/platforms/oneApi.ts`、`veloera.ts`、 -`standardApiProvider.ts`、`src/server/proxy-core/surfaces/*`、 -`src/server/routes/proxy/{completions,embeddings,images,search}.ts`、 -`src/web/pages/Accounts.tsx` 和 `Tokens.tsx`。 - -## 共同验证门 - -每个子任务完成前按变更范围运行: - -```bash -npm run typecheck:web -npm run typecheck:web:test -npm run typecheck:server -npm test -- -npm run repo:drift-check -git diff --check -npm run build:web -``` - -父任务最终再运行: - -```bash -npm test -npm run typecheck -npm run repo:drift-check -npm run build -``` - -若全量测试遇到既有失败,保留完整命令、失败文件、退出码和是否与本任务 -相关的判断,不通过删测试、放宽断言或隐藏 warning 来“修复”。 - -## 回滚点与停止条件 - -- 每个 wrapper、hook、服务抽取和页面迁移保持单一职责、独立 commit; -- 出现 API 响应变化、协议转换差异、焦点/滚动锁回归或跨域测速能力丢失时, - 立即停止该子任务,保留失败证据并回到对应 facade,而不是继续扩大重构; -- 发现数据库 schema/migration 需求、外部实时服务缺失或需要删除用户能力时, - 暂停并回到 PRD 评审,不自行扩大范围; -- 不使用 destructive git 命令,不覆盖用户已有 untracked/dirty 文件。 - -## 完成定义 - -- 三个子任务的验收标准均有测试或可重复的测量证据; -- 页面不直接 import 第三方 UI 原语,新增页面不绕过 `api.ts`(显式跨域/登录 - 例外有 owner 和 tracked debt); -- 已解决的 proxy-core/page boundary debt 从 drift allowlist 中删除; -- 目标页面的重复样式/状态/编排明显下降且没有能力回归; -- 全量检查、构建和文档/规范更新完成后,才可执行 Trellis finish/archive。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/prd.md b/.trellis/tasks/08-31-project-wide-simplification/prd.md deleted file mode 100644 index 02324ac..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/prd.md +++ /dev/null @@ -1,148 +0,0 @@ -# 全项目简化与前端一致性 - -## 目标 - -在保留现有用户能力以及协议/数据库语义的前提下,降低 Metapi 全项目的维护 -成本和行为漂移。项目应拥有一条容易理解的前端路径、一套可复用的视觉语言, -以及明确负责重复请求、状态和业务编排的 owner。所有管理页面的响应式行为、 -加载/错误反馈、键盘行为和交互质量应保持一致。 - -## 已确认的仓库事实 - -- 项目是 TypeScript 单仓库,包含 Fastify 服务端、React 18/Vite 管理后台和 - Electron 桌面封装;Web 路由已在 `src/web/App.tsx:22-39` 使用 lazy load。 -- Web 应用在 `src/web/index.css` 中已有全局 token/class 体系(当前约 6,908 - 行、62 个 CSS variables),但多个页面仍携带大量 inline style。热点包括 `Settings.tsx`、 - `ProxyLogs.tsx`、`Sites.tsx`、`Accounts.tsx`、`Models.tsx` 和 `ModelTester.tsx`。 -- `vite.config.ts` 同时启用 React plugin 和 Tailwind Vite plugin,而生产界面 - 主要使用 `index.css` class 与 inline style;必须通过明确决策解决入口不一致, - 不能让页面各自选择。 -- `src/web/api.ts` 是认证请求/SSE 边界,拥有 timeout、session expiry、错误 - 提取和 proxy-test alias;少数页面仍直接调用浏览器 `fetch()`,需要先审计。 -- 大型编排页面包括 `Settings.tsx`(2,647 行)、`ProxyLogs.tsx`(3,832 行)、 - `Accounts.tsx`(3,499 行)和 `ModelTester.tsx`(3,401 行);`Sites.tsx` 为 - 2,426 行,`Dashboard.tsx` 为 1,710 行。现有领域子目录和响应式 primitive - 证明增量抽取是项目接受的模式。 -- 历史 single-source consolidation(`docs/plans/2026-03-23-single-source-consolidation-mega-plan.md`) - 已覆盖协议归一化、route refresh、platform discovery、schema metadata、 - responsive filter/action shell 以及部分 modal/drawer。新工作必须复用这些 - owner,不得重建并行 helper。 -- 当前工作树含用户拥有的未跟踪 Trellis/IDE/生成文件;它们不属于本任务范围, - 必须原样保留。 -- 兄弟项目 Iris 没有使用 Arco 或 Ant Design 全套视觉组件,而是项目组件 + - Radix Dialog/Tabs 原语、Tailwind token/utility、TanStack React Query、Zustand、 - Lucide、Motion 和 dnd-kit;其 `OverlayDialog` 展示了“第三方行为隐藏在本地 - wrapper 后面”的模式。 -- 用户已选择 Iris-like 组件架构:Radix 原语只负责交互/无障碍,Metapi 保留 - 自己的靛蓝/灰色 token 和领域组件,路由页不得暴露或直接使用第三方组件 API。 - Tailwind 是否保留由实际使用率和构建证据决定,不能因此混用多套页面体系。 -- 规划阶段构建采样为 `2613 modules transformed`、约 `5.35s`,`vchart-vendor` 为 - `2,154.02 kB`(gzip `586.81 kB`);最终复核为 `2614 modules`,这些数字来自 - 本机 dirty worktree,不能直接当作发布基线。 -- `useAsyncResource`、`siteSpeedProbe`、`streamBatcher`、`components/ui/**` 和 - 中性 server helper 目前属于未提交工作树候选/WIP;是否纳入最终交付必须以 - focused tests、typecheck、build、drift 和 review 证据为准。 - -## 需求 - -### R1. 建立可执行的简化契约 - -建立重复样式、页面 raw request、页面互引、异步状态转移和超大编排文件的基线 -清单与 architecture check。新代码必须沿用 React、Vite、TypeScript、 -`src/web/api.ts` 和全局 design token 路径;页面不得引入新的 UI framework、 -CSS module 约定、样式 runtime 或本地 HTTP client,除非有明确记录的例外。 - -### R2. 统一前端视觉体系 - -把现有 CSS variable 和语义 class 作为唯一视觉语言,抽取通用的 page header、 -section、field、loading/error/empty、action group 和 list/table state primitive。 -优先迁移重复频率最高的 inline style,保留现有响应式断点和主题行为。新生产 -UI 不得在已有 shared class/variant 可用时新增 one-off style object。 - -### R3. 收敛异步状态与请求 owner - -提供小而类型安全的可取消加载 helper,负责 stale-result protection、loading/ -error/empty 转移和 refresh invalidation;至少迁移一组高频页面。领域状态仍由 -页面拥有,认证 HTTP/SSE 仍由 `src/web/api.ts` 负责,取消、timeout 和 partial -failure 的用户语义必须保持可区分。 - -### R4. 按职责拆分大型编排文件 - -把应用 shell 和最大页面热点拆成领域 hook、section 与 component,保持页面作为 -编排面;modal/panel 家族放回现有领域目录,纯计算放入 helper。不得创建无主的 -通用 `utils/` 垃圾场,也不得把业务规则搬入 route/page adapter。 - -### R5. 以证据驱动前端性能优化 - -保留路由级 code splitting,并针对列表、图表、搜索、轮询和 model tester 做 -可测量优化:避免重复请求、取消过期工作、延迟非关键数据、稳定昂贵派生值, -减少无关 parent 更新导致的重渲染。记录前后 chunk/request/render 基线,目标 -路径不得回归,并应有可重复的改善或明确的瓶颈转移证据。 - -### R6. 提升跨页面 UX 与可访问性一致性 - -用共享 primitive 统一 focus、keyboard、dialog/drawer 语义、移动 action/filter、 -loading skeleton、empty state、可操作错误和翻译文本;保留 URL intent 与历史 -行为。每个交互家族增加聚焦测试,并覆盖 stale request 和 cleanup。 - -### R7. 只合并有证据的服务端重复 - -审计 services、proxy-core、routes、transformers、contracts 和 platform adapter。 -只有在存在清晰重复和 owner 时才合并,复用既有 workflow/failure vocabulary; -routes 保持 adapter、transformers 保持 protocol-pure。数据库变更不在本任务内, -除非单独发现并获批 schema 需求。 - -### R8. 留下可重复的维护守卫 - -记录选定的前端 stack 和抽取模式,在必要位置添加 architecture test,并纳入 -repository drift gate,防止后续页面悄悄重新引入第二套技术或样式体系。 - -## 验收标准 - -- [ ] 检入一份 baseline/audit,记录重复样式、请求、状态和大文件热点,并把每个 - 选定改动链接到明确 owner。 -- [ ] 前端有一条文档化的生产样式路径;新改页面不得引入独立 CSS/UI 技术或绕过 - shared API boundary。未使用的 build plugin/dependency 必须删除,或有测试和 - 文档说明其保留理由。 -- [ ] shared primitive 与 async request/state helper 有 unit 或 architecture test, - 至少三个高频页面实际使用。 -- [ ] 选定大型页面的编排边界变薄,路由、action、URL intent、响应式行为、翻译及 - modal/drawer 无障碍保证均无损失。 -- [ ] 性能测量证明 initial/route bundle、重复请求数和 stale 行为不回归;目标的 - list/chart/model-test 路径有改善,或有可复现测量证明瓶颈在别处。 -- [ ] 改过的异步路径会取消或 stale-guard 过期工作,恢复 timer/listener/body-scroll, - 并提供可操作的 loading、empty 和 error feedback。 -- [ ] 相关 focused tests、web/server type checks、`npm run repo:drift-check`、 - `git diff --check` 和生产构建通过;既有无关失败必须带精确证据记录,不能隐藏。 -- [ ] 维护者文档说明 canonical frontend stack、shared styles/hooks/components 的放置 - 位置,以及 architecture check 如何阻止页面自行发明实现。 - -## 范围内 - -- 一个带独立验收前端基础、前端性能/UX、服务端简化切片的 parent task; -- 在现有 React/Vite/Fastify 架构内的代码、测试、狭义文档和 architecture check 改动。 - -## 范围外 - -- 删除或重设计用户能力、改变 proxy protocol semantics、database schema/migrations, - 或未经单独批准改变 public API contract; -- 替换 React/Vite、增加全局 state library,或一次重写所有历史页面; -- 清理无关的 untracked 文件、生成物、IDE 状态或既有用户改动。 - -## 关键决策 - -- **组件架构**:采用 Iris-like 的 Radix behavior primitives + 单一项目封装层;保留 - 并扩展已有 Metapi component,不为使用库而替换仍然正确的领域组件。 -- **视觉兼容**:保留现有 Metapi CSS variables、主题、响应式断点和用户可见样式。 -- **第三方 import 边界**:只有 `src/web/components/ui/**` 可以直接 import Radix; - routed pages/domain components 只能依赖项目 wrapper。 -- **样式入口**:先依据 utility 使用率和 production build 证据决定 Tailwind 是否清理; - 无论结果如何,不允许页面增加另一种 CSS runtime 或 convention。 - -当前没有待用户决策的产品问题;Tailwind 的处理结果属于实现前可验证的技术判断, -并会在 `design.md` 中记录证据。 - -## Trellis 文档语言 - -本任务后续新增或修改的 Trellis 文档默认使用简体中文。命令、路径、代码符号、 -配置字段、协议名和第三方名称保持规范原文;已有英文历史内容不因该约定批量回译。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/research/backend-architecture.md b/.trellis/tasks/08-31-project-wide-simplification/research/backend-architecture.md deleted file mode 100644 index eda75a2..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/research/backend-architecture.md +++ /dev/null @@ -1,111 +0,0 @@ -# Metapi 后端架构调查 - -## 查询元数据 - -- **查询**:调查 Fastify route、proxy-core、service、transformer、contract、database 和 platform adapter 的真实所有权,核对重复诊断、proxy log 及 drift debt。 -- **范围**:`src/server/index.ts`、`src/server/routes/**`、`src/server/proxy-core/**`、`src/server/services/**`、`src/server/transformers/**`、`src/server/contracts/**`、`src/server/db/**`、相关测试与 `scripts/dev/repo-drift-check.ts`。 -- **日期**:2026-08-31。 - -## 可复现命令 - -```bash -find src/server -maxdepth 2 -type d | sort -rg -n 'executeEndpointFlow|sharedSurface|readRuntimeResponseText' src/server/routes src/server/proxy-core -rg -n 'class .*Adapter|extends StandardApiProviderAdapterBase|extends BasePlatformAdapter' src/server/services/platforms -rg -n '500000|1000000|Veloera-User|New-API-User|User-id|available_model' src/server/services src/server/routes -rg -n 'diagnoseVerificationFailure|detectVerifyFailureReason|accountVerificationDiagnostics' src/server/routes/api/accounts.ts src/server/services -rg -n 'writeSurfaceProxyLog|logProxy|insertProxyLog|composeProxyLogMessage' src/server/routes/proxy src/server/proxy-core/surfaces -rg -n 'sqliteTable\\(|switchRuntimeDatabase|RuntimeDbDialect' src/server/db src/server/index.ts -npm run repo:drift-check -``` - -## 真实层次和启动编排 - -```text -src/server/routes/api/** 管理 API Fastify adapter -src/server/routes/proxy/** /v1 与供应商兼容 endpoint adapter/facade -src/server/proxy-core/** endpoint flow、surface、executor、provider、runtime -src/server/services/** 业务 workflow、平台 adapter、OAuth、日志、路由 -src/server/transformers/** 协议纯净转换 -src/server/contracts/** Zod 外部 payload parser -src/server/db/** schema、连接、迁移、runtime dialect、兼容处理 -``` - -`src/server/index.ts:135-260` 在创建 Fastify 前确保 runtime DB、读取 settings、切换 dialect、执行 compatibility columns、backfill、route rebuild 和 OAuth registry;随后注册 auth hook、管理 API、proxy routes、静态 web 与 SPA fallback。它是启动/生命周期 owner,不是把 scheduler 或业务流程塞进 route 的理由。 - -## Route、core、service 和 transformer 规则 - -- route 文件可以注册 Fastify endpoint、读取 request context、调用 service/surface 和组装 HTTP response;不能拥有 retry policy、stream lifecycle、billing 或 persistence。 -- endpoint fallback 必须通过 `src/server/proxy-core/orchestration/endpointFlow.ts` 的 `executeEndpointFlow()`;`src/server/routes/proxy/endpointFlow.ts` 只是兼容 re-export。 -- `src/server/proxy-core/surfaces/sharedSurface.ts:239-300` 拥有共享 channel/session 记账、usage、billing、proxy log 和 failure toolkit;整包上游 body 读取通过 `src/server/proxy-core/executors/types.ts` 的 `readRuntimeResponseText()`。 -- `src/server/transformers/` 不得导入 route、Fastify、OAuth service、token router 或 runtime dispatch;协议转换应可独立测试。 -- 外部 request body 在 `src/server/contracts/` 用 Zod parser 归一化,route 只处理 parser 的结果和 HTTP status。 - -## Platform adapter 继承与差异(纠正旧任务文档) - -真实继承关系: - -```text -StandardApiProviderAdapterBase(standardApiProvider.ts) - ├─ ClaudeAdapter - ├─ GeminiAdapter - ├─ OpenAiAdapter - ├─ OrcaRouterAdapter - └─ CliProxyApiAdapter - -BasePlatformAdapter(base.ts) - ├─ OneApiAdapter(oneApi.ts) - │ └─ OneHubAdapter - │ └─ DoneHubAdapter - ├─ VeloeraAdapter(veloera.ts) - ├─ NewApiAdapter - ├─ Sub2ApiAdapter - └─ 其他平台 -``` - -`OneApiAdapter` 与 `VeloeraAdapter` 都直接继承 `BasePlatformAdapter`(分别见 `oneApi.ts:1,19`、`veloera.ts:1,8`),不是 `StandardApiProviderAdapterBase` 的子类。因此不能在设计或 PRD 中写“通过 StandardApiProviderAdapterBase 合并 OneApi/Veloera”,除非先完成独立的中性策略抽取并证明兼容。 - -已经由源码和测试锁定的差异: - -| 平台 | 代码证据 | 语义 | -| --- | --- | --- | -| OneApi | `oneApi.ts:57-81` | check-in `/api/user/checkin`;quota/used/today 字段除以 `500000`;models 从 `/v1/models` 读取并写入 context-length cache | -| Veloera | `veloera.ts:23-58` | headers 可能同时带 `Veloera-User`、`New-API-User`、`User-id`;quota/used/today 除以 `1000000`;models 流程相似但 header contract 不同 | -| OneHub | `oneHub.ts:12-39` | 先 `/v1/models`,失败后 `/api/available_model`;user group 从 `/api/user_group_map` 读取 | -| DoneHub | `doneHub.ts:12-33`、测试 `doneHub.test.ts:78-105` | check-in 明确不支持;`quota` 是 remaining,`used_quota` 是 spent,total 为两者之和;继承 OneHub 的 model fallback | - -可行方向是中性策略/config/helper + 参数化测试,不是强行改继承树。`StandardApiProviderAdapterBase` 当前只拥有 standard models URL、unsupported login/check-in/zero-balance defaults(`standardApiProvider.ts:37-97`)。 - -## 账号验证诊断 - -HEAD `accounts.ts` 同时有 `diagnoseVerificationFailure` 和 `detectVerifyFailureReason` 两套局部实现(`git show HEAD:src/server/routes/api/accounts.ts` 可复现,约 721-861、966-1055 行):两者都生成 Bearer/Cookie variants,访问 `/api/user/self`,解析 shield、`needs-user-id`/`invalid-user-id`,并使用 2,500ms 诊断 timeout;差异在 endpoint pool 和网络错误可见性。 - -当前工作树新增候选 `src/server/services/accountVerificationDiagnostics.ts`: - -- `parseAccountVerificationFailureReason()` 负责纯文本/JSON/shield/user-id 分类(约 105-147 行); -- `buildAccountVerificationHeaderVariants()` 固定 Bearer/Cookie 顺序(约 153-190 行); -- `diagnoseAccountVerificationFailure()` 返回 `reason`、`sawResponse`、`sawNetworkError`、`status`、`endpoint`、`timedOut`、`retryable`(约 215-278 行)。 - -当前 route 已通过 `requireSiteApiBaseUrl(site)` 选择 endpoint,再注入 `withSiteRecordProxyRequestInit`;这是未提交 WIP,必须经过 accounts verify/rebind focused tests 和 architecture test 才能写成完成。 - -## Proxy log 所有权和剩余重复 - -- 共享 owner `writeSurfaceProxyLog()` 位于 `sharedSurface.ts:239-300`,统一 UTC 时间、`composeProxyLogMessage()`、`insertProxyLog()`、usageSource、billing、stream timing、client/session/trace metadata 和 best-effort warning。 -- `sharedSurface.ts` 已被 chat、rerank 等 surface 使用,但 `routes/proxy/completions.ts`、`embeddings.ts`、`images.ts`、`search.ts` 以及 `proxy-core/surfaces/geminiSurface.ts` 仍有 local `logProxy`。迁移前必须逐个对照 `downstreamPath`、`upstreamPath`、`usageSource`、billing 默认值、stream timing、token 默认值、warning scope、status/retry 语义;不能只做名称替换。 -- 当前候选中性 service/facade 为 `downstreamPolicyService.ts`、`multipart.ts`,并有 `shared-helper-boundaries.architecture.test.ts`;它们已让当前 drift checker 结果为 0/0,但仍未提交、未完成完整产品回归。 - -## Drift 与 page boundary - -当前 checker 的 `HEAD` 归档有五条违规:四条 `proxy-core -> routes/proxy`(downstreamPolicy/multipart)和一条 `src/web/pages/Accounts.tsx -> ./Tokens.js`。当前工作树通过中性 service/facade 与 `pages/tokens/` 候选迁移暂时消除这些路径;历史 checker 曾把它们记成 tracked debt。任务材料必须同时写清 checker 版本和扫描对象。 - -## 数据库与测试边界 - -- `src/server/db/schema.ts` 有 27 个 `sqliteTable`,`drizzle/` 有 29 个 SQL 文件;`src/server/db/index.ts` 支持 SQLite/MySQL/PostgreSQL runtime switch。 -- 本轮不改 schema/migration/generated artifacts;schema 需求必须单独建任务并同步 Drizzle schema、migration history、generated contract/parity 测试。 -- 相关验证应覆盖 `src/server/routes/api/accounts*`、`src/server/services/platforms`、`src/server/proxy-core/surfaces`、`src/server/routes/proxy` 的 focused tests、`npm run typecheck:server`、`npm run build:server`、`npm run repo:drift-check` 和 `git diff --check`。Node/native ABI 不匹配时必须精确记录,不能隐藏失败。 - -## 反模式 - -- 不把 route 变成 workflow owner,不在 transformer 中引入 runtime/service,不在 proxy-core 中直接从 routes/proxy 取 helper。 -- 不复制平台 quota/header/check-in 逻辑,也不把不兼容平台伪装成统一 capability。 -- 不在 log migration 中丢失 path/usage/billing/trace 字段,不把 best-effort telemetry 失败变成主请求成功。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/research/frontend-architecture.md b/.trellis/tasks/08-31-project-wide-simplification/research/frontend-architecture.md deleted file mode 100644 index 7039702..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/research/frontend-architecture.md +++ /dev/null @@ -1,76 +0,0 @@ -# Metapi 前端架构调查 - -## 查询元数据 - -- **查询**:确认 React/Vite 入口、页面边界、认证请求 owner、raw `fetch()` 例外、状态管理、样式入口、性能热点和当前候选 helper。 -- **范围**:`src/web/main.tsx`、`src/web/App.tsx`、`src/web/api.ts`、`src/web/components/**`、`src/web/pages/**`、`src/web/index.css`、`vite.config.ts`、前端测试与 `package.json`。 -- **日期**:2026-08-31。 - -## 可复现命令 - -```bash -nl -ba src/web/main.tsx -nl -ba src/web/App.tsx -rg -n '\\bfetch\\(' src/web --glob '*.ts' --glob '*.tsx' -rg -n 'useAsyncResource|siteSpeedProbe|streamBatcher' src/web --glob '*.ts' --glob '*.tsx' -rg -l 'style=\\{\\{' src/web/pages/*.tsx -wc -l src/web/index.css src/web/pages/*.tsx -npm run typecheck:web -npm run typecheck:web:test -npm run build:web -``` - -## 已确认的生产入口 - -- `src/web/main.tsx:1-13` 使用 `React.StrictMode` + `BrowserRouter` 挂载 ``,并加载 `index.css`。 -- `src/web/App.tsx:22-39` 通过 `lazy(() => import(...))` 注册 Dashboard、Sites、Accounts、Tokens、TokenRoutes、ProxyLogs、Settings、DownstreamKeys、Models、ModelTester 等路由;`App.tsx` 还拥有 shell、`I18nProvider`、`ToastProvider`、登录状态和导航。 -- `package.json` 当前为 React `18.3.1`、Vite `6.4.2`、TypeScript `6.0.3`;UI 依赖包含 `@radix-ui/react-dialog` 和 `@visactor/react-vchart`。仓库没有 React Query、SWR、Redux 或 Zustand 依赖,server state 主要由 React state/Context + 手写 API method 管理。 - -## API 边界与 raw `fetch()` 分类 - -`src/web/api.ts` 是已认证管理 API 的默认 owner,负责 auth token、401/403 session 清理、timeout、外部 `AbortSignal`、body 生命周期、HTTP error extraction、JSON/SSE/binary response,以及 proxy-test alias(见 `api.ts:10-34`、`200-240`、`447-500`、`1912-1990`)。但“所有请求都经过 `api.ts`”不符合当前源码,必须保留以下有 owner 的例外: - -| 文件/行 | 例外 | 为什么不能机械归入普通 wrapper | 必须保留的契约 | -| --- | --- | --- | --- | -| `src/web/App.tsx:150-181` | 登录 bootstrap | 此时 token 尚未持久化,登录页需要用用户输入的 token 探测公开 auth info | Authorization header、错误文本提取、登录失败翻译;后续管理请求才进入 `api.ts` | -| `src/web/pages/Sites.tsx:634-690` | `probe-stream` | 页面需要自己的 `AbortController` 和 `ReadableStream` 增量读取 | 每站取消、stream close、超时和逐项结果;不能把 no-cors/stream 误当普通 JSON | -| `src/web/api.ts:447-500` | client 内部 `fetch` | 这是 API boundary 的实现,不是页面绕过 | 统一 auth、timeout、错误和 body cleanup | -| `src/web/api.ts:1912-1990` | `testChatStream` 返回原始 `Response` | 调用方有意拥有 stream body 生命周期 | 明确 `wrapBody`/reader 责任,不能重复消费 body | - -因此规范应写成“已认证管理 API 默认经过 `api.ts`;raw `fetch()` 仅在列出 owner、浏览器/跨域/stream 原因、auth、timeout、cancellation、测试和迁移边界时允许”,而不是禁止全部 raw `fetch()`。 - -## 状态与生命周期模式 - -- 当前 canonical state 仍是页面本地 `useState`、`useMemo`、`useRef` 和两个 Context;URL intent 由 React Router location/search 管理,安装级状态由 `authSession.ts`、`appLocalState.ts` 等小模块管理。 -- `useIsMobile.ts`、`useAnimatedVisibility.ts`、`CenteredModal.tsx`、`MobileDrawer.tsx` 展示 listener/timer/animation/body-scroll cleanup 模式。 -- `TokenRoutes.tsx` 已有 progressive chunk + `IntersectionObserver`;这是评估大列表优化的样例,不是所有列表都应机械窗口化。 -- 过期请求必须由 cancelled flag、sequence ref 或 `AbortController` 保护;mutation 后刷新权威 snapshot,不能把派生 filter/sort 复制成第二份可变 server state。 - -## 样式与 bundle 事实 - -- `src/web/index.css` 当前实测 6,908 行、62 个 CSS variables,约 943 个以行首 `.` 统计的 class selector;页面大量组合语义 class 和少量 inline style。 -- `vite.config.ts` 同时配置 React plugin 与 Tailwind Vite plugin,`index.css:1` 有 `@import "tailwindcss"`。 -- 生产源码明确可见的 utility 至少有 `Settings.tsx` 与 `DownstreamKeys.tsx` 的 `h-4 w-4`;因此不能只凭“看起来很少”删除 Tailwind。应先完成 utility 使用率、生成 CSS 和 chunk 的可重复审计,再决定保留/清理。 -- 规划阶段构建采样为 `2613 modules`、`5.35s`、`vchart-vendor 2,154.02 kB`(gzip `586.81 kB`);最终交付复核为 `2614 modules`、CSS `136.83 kB`(gzip `22.01 kB`)。图表 below-fold gate 必须以网络请求、布局稳定和图表出现后的功能测试为准。 - -## 当前工作树候选实现(未提交) - -以下文件属于当前工作树 WIP,不等于 HEAD 或最终验收: - -- `src/web/components/useAsyncResource.ts`:单活动请求、abort、递增 request id、mounted guard、silent refresh、dedupe、`AsyncErrorKind` 分类。当前工作树生产引用为 `Dashboard.tsx`、`Models.tsx`、`ProxyLogs.tsx`、`SearchModal.tsx` 四条路径;HEAD 没有该 helper。必须完成 focused test、typecheck、review 后才能计入验收。 -- `src/web/pages/helpers/siteSpeedProbe.ts`:保留 `mode: "no-cors"`,每站 timeout/abort,默认并发 4,`probeSiteSpeeds()` 逐项回调。该模块是有意的跨域 browser-only 例外,不应伪装成 `api.ts` 管理请求。 -- `src/web/pages/model-tester/streamBatcher.ts`:24ms 窗口,分别保留 delta/raw event 顺序,支持 `flush()`/`dispose()`;ModelTester 在停止、abort、完成和卸载路径显式 flush。必须用内容等价和 render/flush 计数验证,而不是只看编译。 -- `SearchModal.tsx` 当前 WIP 已使用 `useAsyncResource`,通过 query ref + debounce timer 保护 q1→q2 stale 竞态;这覆盖了旧调查中“尚未迁移”的结论。 - -## 反模式与维护规则 - -- 顶层 page 不得互相 import;领域 modal/drawer/panel 放入 `pages//`,共享 UI 放入 `components/` 或 `components/ui/`。 -- 不新增第二套 CSS module/styled-components/runtime,不把业务规则搬入 UI wrapper,不为单页 server state 引入 global store。 -- 新 effect 必须列出依赖并清理 timer/listener/reader/body lock;后台轮询要有单飞或 abort previous 与 `visibilitychange` 语义。 -- 新 UI 使用真实 button/link、label、dialog role/aria 和 `t()`/`tr()`;测试应断言可见行为、键盘和 cleanup,不只断言 mock 调用。 - -## HEAD、工作树与限制 - -- HEAD 的 `SearchModal`、Dashboard、Models、ProxyLogs 使用各自的局部请求/状态逻辑;当前工作树已出现候选抽取,但产品改动还未完成全量回归。 -- 本轮未运行真实浏览器 performance trace、生产网络或用户端 LCP/TTI;build 数字只作为本机可复现基线。 -- Node 当前为 `v22.23.1`,而 package engine 为 `>=25.0.0`;完整前端/产品验收要在匹配运行时复核。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/research/repository-baseline.md b/.trellis/tasks/08-31-project-wide-simplification/research/repository-baseline.md deleted file mode 100644 index 50dd14d..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/research/repository-baseline.md +++ /dev/null @@ -1,93 +0,0 @@ -# Metapi 仓库基线调查 - -## 查询元数据 - -- **查询**:调查当前仓库的规模、运行时入口、依赖、测试门禁、数据库形态、Git 基线和 Trellis 任务状态。 -- **范围**:`src/`、`drizzle/`、`scripts/`、`package.json`、`.trellis/`、`.codex/`;不修改产品代码,不把未提交产品 WIP 当作已验收结果。 -- **日期**:2026-08-31(Asia/Shanghai)。 -- **调查者**:`cita-777` session;当前分支为 `dev`。 - -## 可复现命令 - -以下命令在仓库根目录 `/Users/apple/code/metapi` 执行: - -```bash -python3 ./.trellis/scripts/get_context.py -python3 ./.trellis/scripts/get_context.py --mode phase -python3 ./.trellis/scripts/get_context.py --mode packages -git status --short -git log --oneline -5 -npm run repo:drift-check -npm run typecheck:web -npm run build:web -git diff --check -``` - -规模统计使用 `Path.rglob('*.ts'/'*.tsx')` 计算文件数和物理行数;CSS、页面热点和 `sqliteTable(` 使用 `rg`/`wc` 复核。构建数字来自本轮实际的 `vite build` 输出,不沿用旧任务文档中的估算。 - -## Git 与 Trellis 状态 - -- `HEAD`:`f4114ada6d797e668c83c1b3f68ca80f8c897863`,提交说明为 `fix: tolerate corrupted site announcement cache rows`。 -- 当前工作树有约 71 个未提交路径,包含用户产品 WIP、`.agents/`、`.codex/`、`.trellis/`、IDE 状态和生成文件。它们均视为用户已有内容;本调查不清理、不覆盖、不提交。 -- `.trellis/scripts/get_context.py` 报告为 single-repo,spec layers 为 `backend`、`frontend`;当前 session 没有自己的 `CURRENT TASK` 指针,但存在四个活动任务:一个 parent planning、两个 child in_progress、一个 child planning。 -- 不要在本 session 为了制造指针运行 `task.py start`;其他 session 正在处理两个 child task,活动任务指针是 session-specific 的。 - -## 规模快照 - -本轮工作树实测如下(生成文件是否存在会影响少量行数): - -| 路径 | TypeScript/TSX 文件 | 行数 | -| --- | ---: | ---: | -| `src/server` | 648 | 187,574 | -| `src/web` | 280 | 77,035 | -| `src/desktop` | 7 | 912 | -| `src/shared` | 17 | 796 | -| `src/server/routes/api` | 79 | 34,230 | -| `src/server/routes/proxy` | 53 | 23,647 | -| `src/server/proxy-core` | 70 | 15,159 | -| `src/server/services` | 251 | 66,102 | -| `src/server/transformers` | 119 | 34,441 | -| `src/server/contracts` | 7 | 1,325 | -| `src/server/db` | 38 | 8,401 | -| `src/web/components` | 62 | 6,621 | -| `src/web/pages` | 193 | 64,697 | - -页面热点(`wc -l`)为:`ProxyLogs.tsx` 3,832、`Accounts.tsx` 3,499、`ModelTester.tsx` 3,401、`Settings.tsx` 2,647、`Sites.tsx` 2,426、`TokenRoutes.tsx` 2,041、`Dashboard.tsx` 1,710、`DownstreamKeys.tsx` 1,415、`Models.tsx` 1,124、`OAuthManagement.tsx` 2,601。`style={{` 的文件级热点为 `Settings.tsx` 192、`ProxyLogs.tsx` 185、`Sites.tsx` 134、`Models.tsx` 119、`Accounts.tsx` 109、`ModelTester.tsx` 108、`DownstreamKeys.tsx` 82、`Dashboard.tsx` 64。 - -## 依赖和构建基线 - -- `package.json` 声明 Node `>=25.0.0`;本次本地命令使用 Node `v22.23.1`,因此完整产品验收不能只凭本地通过推断。 -- 关键版本:React `18.3.1`、Vite `6.4.2`、Fastify `5.8.4`、Drizzle ORM `0.45.2`、`@radix-ui/react-dialog` `1.1.23`、`@visactor/react-vchart` `2.0.22`、Tailwind CSS `4.0.0`。 -- 规划阶段的 `npm run build:web` 采样为:`2613 modules transformed`,约 `5.35s` 完成;CSS 为 `136.78 kB`(gzip `22.00 kB`),`vchart-vendor` 为 `2,154.02 kB`(gzip `586.81 kB`)。较大的路由 chunk 为 `TokenRoutes` `176.61 kB`、`Settings` `105.96 kB`、`ModelTester` `88.98 kB`、`DownstreamKeys` `65.48 kB`、`Accounts` `60.73 kB`、`Sites` `59.67 kB`、`ProxyLogs` `58.56 kB`;最终交付复核见 `docs/frontend-architecture.md` 与任务审计。 -- 旧 `design.md` 记录的 `2548 modules/5.30s/2,145.97 kB` 已过时;后续报告应标明采样时间和工作树状态。 -- `npm run repo:drift-check` 当前工作树实测为 `Violations: 0`、`Tracked debt: 0`。这只证明当前 checker 对当前文件集的结果,不证明未提交 WIP 已通过产品回归。 - -## Drift 基线口径 - -为避免把两个时期混为一谈,保留三种口径: - -1. **当前工作树 + 当前 checker**:`0 violations / 0 tracked debt`。 -2. **当前 checker 扫描 `HEAD` 归档**:`5 violations / 0 tracked debt`,路径为 `chatSurface.ts -> routes/proxy/downstreamPolicy.js`、`filesSurface.ts -> routes/proxy/multipart.js`、`geminiSurface.ts -> routes/proxy/downstreamPolicy.js`、`openAiResponsesSurface.ts -> routes/proxy/downstreamPolicy.js`、`Accounts.tsx -> ./Tokens.js`。 -3. **历史 HEAD checker + 历史 allowlist**:早期任务材料把这五项记成 tracked debt。若要复现历史数字,必须使用 `HEAD` 版本的 `scripts/dev/repo-drift-check.ts`;不能用当前已删除 allowlist 的 checker 反推历史 debt 数字。 - -临时 HEAD 归档位于 `/tmp/metapi-head-JBtZz7`,仅用于本轮只读比较,不能写入任务 manifest,也不能视为发布产物。 - -## 数据库快照 - -- `src/server/db/schema.ts` 当前有 27 个 `sqliteTable` 声明;`drizzle/` 有 29 个 SQL migration 文件。 -- migration 文件名存在两个 `0027`:`0027_site_custom_headers_override_request_headers.sql` 与 `0027_site_max_concurrency.sql`。这属于需要在 schema 任务中专门处理的事实,本轮不改名、不重排。 -- `src/server/db/index.ts` 暴露 `RuntimeDbDialect = 'sqlite' | 'mysql' | 'postgres'`、`switchRuntimeDatabase()`、transaction wrapper 和 compatibility-column bootstrap;`src/server/index.ts:135-193` 在读取设置后执行 runtime DB 切换、兼容列、默认数据、OAuth backfill 和 route rebuild。 -- 本轮没有修改 schema、migration 或 `src/server/db/generated/`;任何必须改 schema 的发现都应另建任务。 - -## 质量证据与限制 - -- `npm run typecheck:web` 和 `npm run build:web` 已在当前工作树执行并通过;构建前自动运行了 `desktop:icons`。 -- `npm run repo:drift-check` 与 `git diff --check` 是文档/边界工作的最低门禁。 -- 完整 `npm test` 不能在 Node 22 + 当前本机 native dependency 组合下直接当作产品验收;历史记录显示 `better-sqlite3` 可能出现 `NODE_MODULE_VERSION` 不匹配。需要在声明的 Node 版本下重建 native module 后再判断全量结果。 -- 本轮没有真实生产网络、浏览器性能 trace、MySQL/PostgreSQL live service 或部署证据;chunk 数字是本地构建测量,不能替代线上 LCP/TTI 或服务健康证明。 - -## 对后续规范的约束 - -- 研究文档必须区分 `HEAD` 已确认事实、当前工作树事实、未提交候选实现和最终验收状态。 -- 任何“已完成迁移”“debt 已清零”“性能已改善”的表述都必须附带对应测试、构建、drift 或运行时证据;仅有 source diff 不足以升级状态。 -- 文档中的命令、路径、函数名、配置字段、协议名和 `status` 值保持原文;解释性正文使用简体中文。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/research/trellis-runtime.md b/.trellis/tasks/08-31-project-wide-simplification/research/trellis-runtime.md deleted file mode 100644 index 4b55b87..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/research/trellis-runtime.md +++ /dev/null @@ -1,95 +0,0 @@ -# Metapi Trellis 运行时调查 - -## 查询元数据 - -- **查询**:调查本地 Trellis 的 workflow parser、status/breadcrumb 映射、Codex dispatch、按 session 隔离的活动任务、任务生命周期、JSONL manifest、workspace journal 和 Codex hooks 输入输出。 -- **范围**:`.trellis/workflow.md`、`.trellis/scripts/common/active_task.py`、`task_store.py`、`task_context.py`、`task.py`、`session_context.py`、`add_session.py`、`developer.py`、`.codex/hooks/*.py`、`.codex/hooks.json`、`.trellis/config.yaml`。 -- **日期**:2026-08-31。 - -## 可复现命令 - -```bash -python3 ./.trellis/scripts/get_context.py -python3 ./.trellis/scripts/get_context.py --mode phase --platform codex -python3 ./.trellis/scripts/task.py --help -python3 ./.trellis/scripts/task.py current --source -rg -n '\\[workflow-state:' .trellis/workflow.md -nl -ba .codex/hooks/inject-workflow-state.py -nl -ba .codex/hooks/session-start.py -nl -ba .codex/hooks/inject-subagent-context.py -python3 -m compileall -q .trellis/scripts .codex/hooks -``` - -## Workflow parser/tag 契约 - -- `.trellis/workflow.md` 是自然语言工作流和 breadcrumb body 的单一事实来源;`.codex/hooks/inject-workflow-state.py:189-219` 用正则解析 `[workflow-state:STATUS]`,`STATUS` 只能是 `[A-Za-z0-9_-]+`,开闭标签必须使用同一个值。 -- `build_breadcrumb()`(约 `346-367` 行)按 `breadcrumb_key` 查 body;缺少标签或 workflow 文件时退化为通用提示。该 fallback 必须是简体中文,例如“请查阅 `.trellis/workflow.md` 确认当前步骤”,但不能改变 `` 包装、status header 或解析正则。 -- 初始调查时文件只有 `no_task` 标签,活动任务状态无法获得对应详细 body;随后已在本地 `workflow.md` 补齐 `planning`、`planning-inline`、`in_progress`、`in_progress-inline`、`completed` block。当前状态应以 `rg -n '\[workflow-state:' .trellis/workflow.md` 的实时结果为准。 -- status 到 breadcrumb key 的约定:非 Codex 使用 `status`;Codex `dispatch_mode=auto` 使用普通 `status`,`inline` 使用 `${status}-inline`;旧值 `sub-agent` 归一化为 `auto`;非法显式值归一化为 `inline`(`inject-workflow-state.py:273-343`)。 -- `no_task` 是没有活动任务的伪状态,不来自 `task.json.status`;stale pointer 会形成 `stale_`,因此标签正则必须允许下划线和连字符。 - -## Codex dispatch 与上下文顺序 - -- `.trellis/config.yaml` 的 `codex.dispatch_mode` 默认 `auto`;`inject-workflow-state.py:296-323` 生成 `` banner,说明 `auto` 优先 dispatch Trellis sub-agent,主 session 仍负责协调、澄清、更新规范、commit 和 finish;`inline` 由主 session 直接实现/检查。 -- `.codex/hooks/inject-subagent-context.py` 处理 native `SubagentStart` 与平台 Task 输入;实现/检查 agent 读取顺序为 JSONL manifest → `prd.md` → `design.md`(若有)→ `implement.md`(若有),并按角色加载 spec/research。Codex 不应把继承 parent transcript 当作唯一上下文来源。 -- Trellis 本身不注册 MCP server;`.codex/config.toml` 和 hooks 只负责本地 context injection/agent wiring。不要把全局 host 的 MCP 警告归因于 Trellis runtime。 - -## 按 session 隔离的活动任务(Session-specific active task) - -- `.trellis/scripts/common/active_task.py:1-7` 明确说明 active task 是按 AI session/window 存储的指针,目录为 `.trellis/.runtime/sessions/`;没有稳定 session key 就没有可持久化的 active task。 -- context key 优先来自 `TRELLIS_CONTEXT_ID`,也可由平台已验证的 session/conversation/transcript 环境变量解析(约 `37-149`、`294-322` 行)。脚本会 sanitize key 并限制长度,避免把原始路径直接当文件名。 -- `task.py create` 在写入 task 目录后 best-effort 调用 `set_active_task`;有 session identity 时只影响当前 session,缺失时仍创建 `status=planning` 任务但不写全局指针。 -- `task.py start`(`task.py:73-140`)设置当前 session 指针并把 `planning` 改为 `in_progress`;无 session identity 时进入 degraded mode,只改变 `task.json.status` 并提示当前 session 没有持久指针。`finish` 清除当前 session 文件,不改变 task status。 -- 指针指向不存在目录或无法读取 task 时应显示 stale/unknown,不能静默选择另一个 task;修复 stale pointer 应使用 `task.py finish` 或重新 `start`,不要手改 `.runtime/sessions/`。 - -## 任务生命周期与机器字段(Task lifecycle) - -| 命令 | 真实副作用 | 保持不变的字段/语义 | -| --- | --- | --- | -| `task.py create` | 创建 `.trellis/tasks/MM-DD-slug/`、`task.json`、默认 `prd.md`;按平台条件 seed JSONL;可建立 parent/child;best-effort 激活 session | `status=planning`、`id/name/title/parent/children/meta` 等 JSON key | -| `task.py start ` | 设置 session pointer;将 `planning` 写为 `in_progress`;运行 `after_start` hook | `status` 值、路径解析和 hook 输入 `TASK_JSON_PATH` | -| `task.py finish` | 清除当前 session pointer;运行 `after_finish` hook(若 task 仍可解析) | 不把 finish 当作 completed;不自动改 task status | -| `task.py archive ` | 将 task 标记 `completed`、写 `completedAt`、清除仍指向它的 session、移动到 `archive/YYYY-MM/`;按配置可 auto-commit;运行 `after_archive` hook | `completed` 是 archive writer 写入的 status;目录移动和 parent/child 处理顺序 | - -`task.py create` 的 `--no-start` 只跳过 session 激活,不跳过目录和 status 写入。归档前的 branch stale 只 warning,不阻塞;`session_auto_commit: false` 时脚本不能触碰 Git。 - -## JSONL seed/manifest 契约 - -- `task_store.py:117-174` 只在检测到 sub-agent-capable platform(Codex `dispatch_mode=auto` 或配置目录)时创建 `implement.jsonl`、`check.jsonl`。seed 是一行 `{"_example": "..."}`,没有 `file` 字段,因此消费者会跳过它。 -- `_SEED_EXAMPLE` 的自然语言应使用简体中文,但 JSON key `_example`、`file`、`type`、`reason` 和路径保持原文。seed 不是 curated entry;规划阶段必须补至少一个真实 `{"file":"...","reason":"..."}`。 -- `task_context.py:226-314` 对每行 JSONL 做解析;空行/seed 行跳过,非法 JSON、缺失文件或目录是 error;代码文件路径只产生 hygiene warning,spec/research 之外的产品代码不应预注册到 manifest;超出 context byte cap 只 warning。 -- `list-context` 展示 `implement.jsonl`/`check.jsonl` 的真实条目和 `reason`;`validate` 的输出是诊断接口,中文化不能改变退出码或 JSONL 解析。 - -本任务父 manifest 必须引用四份 research 与相关 spec;不得引用 `src/**` 产品代码作为上下文条目。子任务 manifest 可以按实际领域追加 research,但理由使用简体中文。 - -## 工作区与 journal 契约(Workspace/journal contract) - -- `.trellis/workspace//journal-N.md` 是 append-only session log;`add_session.py` 在约 2000 行时创建下一个文件,并更新 `index.md` 的 `@@@auto:*` managed regions。 -- 旧 `index.md` 可能仍有英文列名;`add_session.py:94-105` 同时识别 `Total Sessions` 与 `会话总数`,因此中文化生成模板时必须保留旧英文读取兼容。 -- journal rotation、session number、branch resolution、auto-commit scope 和 `.gitattributes` 合并行为是机器/协作语义;只翻译标题、提示和自然语言,不改 marker、列数或路径。 - -## Hook 输入输出契约 - -- `.codex/hooks.json` 将 `inject-workflow-state.py` 绑定到每轮 UserPromptSubmit 等价事件,将 `inject-subagent-context.py` 绑定到 `SubagentStart`;`session-start.py` 输出 Codex hook JSON。 -- hook 从 stdin 读取 JSON;缺失、空白、malformed 或非 object 输入先安全归一化为空 dict,不能抛出未处理异常,也不能阻塞等待未关闭 stdin。在 Trellis 目录中,空 dict 会按“无活动任务”流程输出 breadcrumb;非 Trellis 目录则无输出。输出必须仍是合法 JSON,`ensure_ascii=False` 允许中文。 -- `session-start.py` 输出 `{suppressOutput, systemMessage, hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:"..."}}`;`inject-workflow-state.py` 输出 `hookSpecificOutput.additionalContext` 或 Kiro 所需纯文本分支;`inject-subagent-context.py` 保持 role、task path、curated context 和平台输入 schema。 -- 用户可见 fallback、status 摘要、``、``、agent prompt 自然语言默认使用简体中文;`hookEventName`、XML-like tag、JSON key、status 值、命令和路径保持原文。 -- skip 条件(`TRELLIS_HOOKS=0`、`TRELLIS_DISABLE_HOOKS=1`、`no-trellis` keyword、非 Trellis 目录)必须保持静默退出或既有退出码;中文化不能让 hook 在这些分支输出额外文本。 - -## 运行时反模式与验证 - -- 不在 hook 中另造与 workflow.md 冲突的 fallback status 字典;parser 只读取 tag body,缺失时报告可修复的中文 generic hint。 -- 不把 Codex `dispatch_mode` 写成新的 status;dispatch mode 与 workflow state 是两个独立 contract。 -- 不用全局 `.current-task` 替代 session pointer,不直接编辑 `.runtime/sessions/`,不把 `finish` 当作 archive。 -- 修改 workflow tag 后运行: - -```bash -python3 ./.trellis/scripts/get_context.py --mode phase --platform codex -python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-31-project-wide-simplification -python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-31-frontend-ui-foundation -python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-31-frontend-performance-ux -python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-31-server-boundary-simplification -python3 -m compileall -q .trellis/scripts .codex/hooks -``` - -另加静态审计:逐行解析所有 JSONL、检查 spec/research Markdown 链接和路径、搜索 placeholder/TODO、检查上游模板词是否误入 Metapi 规范,以及将 hook stdout 送入 `json.loads`。本轮不把已安装的上游英文 skill/template 批量翻译;只有实际修改的本地 Trellis 文档和 hook 文案按中文约定更新。 diff --git a/.trellis/tasks/08-31-project-wide-simplification/task.json b/.trellis/tasks/08-31-project-wide-simplification/task.json deleted file mode 100644 index ee770ec..0000000 --- a/.trellis/tasks/08-31-project-wide-simplification/task.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "id": "project-wide-simplification", - "name": "project-wide-simplification", - "title": "全项目简化与前端一致性", - "description": "采用 Iris-like 的 Radix 原语 + 项目封装层,分阶段收敛前端 UI、请求性能/UX 与服务端边界重复,保持现有能力和协议/数据库语义不变。", - "status": "planning", - "dev_type": null, - "scope": "web-server", - "package": null, - "priority": "P2", - "creator": "cita-777", - "assignee": "cita-777", - "createdAt": "2026-08-31", - "completedAt": null, - "branch": null, - "base_branch": "main", - "worktree_path": null, - "commit": null, - "pr_url": null, - "subtasks": [], - "children": [ - "08-31-frontend-ui-foundation", - "08-31-frontend-performance-ux", - "08-31-server-boundary-simplification" - ], - "parent": null, - "relatedFiles": [], - "notes": "", - "meta": { - "architecture": "iris-like-radix-wrappers", - "planning_baseline": "2026-08-31" - } -} diff --git a/.trellis/tasks/08-31-server-boundary-simplification/check.jsonl b/.trellis/tasks/08-31-server-boundary-simplification/check.jsonl deleted file mode 100644 index 997e0b4..0000000 --- a/.trellis/tasks/08-31-server-boundary-simplification/check.jsonl +++ /dev/null @@ -1,4 +0,0 @@ -{"file":".trellis/spec/backend/quality-guidelines.md","reason":"检查 route/service/transformer 边界"} -{"file":".trellis/spec/backend/error-handling.md","reason":"检查 status、timeout 和 best-effort"} -{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"检查协议和数据库行为不变"} -{"file":".trellis/spec/guides/documentation-language.md","reason":"检查新增任务说明是否遵循中文文档约定"} diff --git a/.trellis/tasks/08-31-server-boundary-simplification/design.md b/.trellis/tasks/08-31-server-boundary-simplification/design.md deleted file mode 100644 index a80d1e9..0000000 --- a/.trellis/tasks/08-31-server-boundary-simplification/design.md +++ /dev/null @@ -1,51 +0,0 @@ -# 服务端边界简化:设计 - -## 账号验证诊断 - -service 接受规范化的候选 base URL、凭据/header 变体和 timeout 选项,返回 -结构化诊断结果(reason、是否收到响应、HTTP status、是否需要 user id、可重试 -信息)。route 只负责选择 endpoint pool 和把结果映射为现有 HTTP 响应。必须 -保留 `sawNetworkError && !sawResponse` 等现有语义,不能用 catch-all 把 timeout -变成“未知”。 - -## OneApi/Veloera 共同流程 - -两者都直接继承 `BasePlatformAdapter`;`StandardApiProviderAdapterBase` 属于 -另一条 provider 继承线,不能直接作为共同基类。若重复流程确实可收敛,应在 -中性 service/策略模块中定义可选策略对象: - -```ts -type StandardApiAdapterOptions = { - buildAuthHeaders?: (credential: unknown) => Record; - quotaDivisor?: number; - checkin?: { successMessage?: string; unsupported?: boolean }; -}; -``` - -OneApi/Veloera 的 adapter 或 service 只声明可验证的差异;平台探测、特殊 header、 -quota divisor、models fallback、check-in capability 和错误文案仍由平台模块拥有, -避免中性抽象吞掉平台特性。必须先补参数化测试,再决定是否落地抽取。 - -## Proxy log 所有权 - -使用命名字段的结构化输入,不再传位置参数或 route-local `any`: - -```ts -type ProxyLogRecordInput = { - request: FastifyRequest; - path: string; - usage?: ProxyUsage; - usageSource?: string; - billing?: ProxyBillingDetails; - // downstream/session/trace fields remain explicit -}; -``` - -owner 负责 UTC 时间、消息组合、insert 和 best-effort warning;调用方保留业务 -上决定的默认值和是否记录的条件。 - -## 边界债务(Boundary debt) - -把 downstream policy 的 request/auth 适配与纯策略计算分离;把 multipart parser -放到 proxy-core/shared 或 service 中;把 TokensPanel 放进 `pages/tokens/` -或中性组件目录。更新 drift allowlist 只在真实迁移完成后进行。 diff --git a/.trellis/tasks/08-31-server-boundary-simplification/implement.jsonl b/.trellis/tasks/08-31-server-boundary-simplification/implement.jsonl deleted file mode 100644 index 3f3c60d..0000000 --- a/.trellis/tasks/08-31-server-boundary-simplification/implement.jsonl +++ /dev/null @@ -1,6 +0,0 @@ -{"file":".trellis/spec/backend/directory-structure.md","reason":"service/route/proxy 所有权"} -{"file":".trellis/spec/backend/error-handling.md","reason":"诊断和日志错误语义"} -{"file":".trellis/spec/backend/quality-guidelines.md","reason":"架构测试和类型检查"} -{"file":".trellis/spec/guides/code-reuse-thinking-guide.md","reason":"服务端重复收敛"} -{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"路由、服务和代理契约"} -{"file":".trellis/spec/guides/documentation-language.md","reason":"保持 Trellis 实施文档和清单使用简体中文"} diff --git a/.trellis/tasks/08-31-server-boundary-simplification/implement.md b/.trellis/tasks/08-31-server-boundary-simplification/implement.md deleted file mode 100644 index c9b116a..0000000 --- a/.trellis/tasks/08-31-server-boundary-simplification/implement.md +++ /dev/null @@ -1,23 +0,0 @@ -# 服务端边界简化:执行清单 - -1. 为 accounts 诊断抽取纯 helper/service 和 table-driven tests;先保持 route - 输出不变,再删除重复实现。 -2. 核对 `OneApiAdapter`/`VeloeraAdapter` 的真实继承关系和平台差异;先以中性 - 策略与参数化 adapter 测试验证,再决定是否抽取共同流程,不强行改为 - `StandardApiProviderAdapterBase` 子类。 -3. 统一 shared `recordProxyLog`,逐个迁移 Gemini/completions/embeddings/images/ - search,运行每个 endpoint 的 usage/billing/stream 回归。 -4. 迁移 downstream policy、multipart helper 和 TokensPanel;删除对应 drift - allowlist,扩展 architecture test 防止回归。 -5. 运行: - -```bash -npm test -- src/server/routes/api/accounts* src/server/services/platforms src/server/proxy-core/surfaces src/server/routes/proxy -npm run typecheck:server -npm run repo:drift-check -npm run build:server -git diff --check -``` - -停止条件:协议 payload、错误 status、网络超时分类、proxy log 字段或数据库 -行为变化时,恢复 facade/旧 owner 并回到父任务评审;不修改 schema。 diff --git a/.trellis/tasks/08-31-server-boundary-simplification/prd.md b/.trellis/tasks/08-31-server-boundary-simplification/prd.md deleted file mode 100644 index 63763f7..0000000 --- a/.trellis/tasks/08-31-server-boundary-simplification/prd.md +++ /dev/null @@ -1,60 +0,0 @@ -# 服务端边界简化 - -## 目标 - -在既有 single-source consolidation 之上,消除仍然存在且有证据的服务端重复 -编排和 proxy-core 边界债务;保持协议转换、路由策略、权限、数据库和错误语义 -不变,让 route 只做适配、service/workflow 才拥有业务流程。 - -## 已确认事实 - -- `accounts.ts` 的 `diagnoseVerificationFailure` 与 `detectVerifyFailureReason` - 两段逻辑都解析 shield/user-id、构造 Bearer/Cookie 变体并请求 - `/api/user/self`,只在 endpoint pool 和网络错误语义上有差异。 -- `oneApi.ts` 与 `veloera.ts` 重复 check-in、balance、models/context cache; - 差异主要是 header、quota divisor 和文案。 -- Gemini、completions、embeddings、images、search 中重复 - `formatUtcSqlDateTime → composeProxyLogMessage → insertProxyLog → catch warn`; - `sharedSurface.ts` 已有可复用 owner 但并非所有路径使用。 -- 当前工作树运行 `repo:drift-check` 为 `0 violations / 0 tracked debt`;同一 - checker 只读扫描 `HEAD` 为 `5 violations / 0 tracked debt`(4 条 - proxy-core → routes/proxy import,1 条 Accounts → Tokens page import)。任务 - 记录必须标明扫描对象,不能把历史违规写成当前 debt。 - -## 需求 - -1. 抽取中性 `accountVerificationDiagnostics`(或等价 owner),统一两套验证 - 诊断,保留 endpoint pool、`requireSiteApiBaseUrl`、network timeout、shield、 - user-id reason 和错误可见性。 -2. 先核对平台继承关系:`OneApiAdapter` 与 `VeloeraAdapter` 当前都直接继承 - `BasePlatformAdapter`,不是 `StandardApiProviderAdapterBase` 的子类。只有在 - 中性策略 owner 和参数化测试证明兼容后,才考虑抽取共同流程;不得为了“统一” - 强行改变继承树。header、quota divisor、check-in、models/context cache 的 - 差异必须显式保留。 -3. 统一结构化 `recordProxyLog` helper,迁移 proxy log 重复路径,保留每个 - endpoint 的 path、usageSource、billing/token 默认值和 warning scope。 -4. 将 `downstreamPolicy`、multipart helper 等从 routes/proxy 移到正确的中性 - owner;迁移 Accounts TokensPanel 到 domain 子目录,删除已解决的 tracked debt。 -5. 每个抽象都有参数化/架构回归测试;不增加 `any`、不改变 HTTP 状态或协议 - payload,不把 route 变成新的业务 owner。 - -## 验收标准 - -- [ ] accounts 验证诊断单一 owner 覆盖 shield、needs/invalid user-id、 - Bearer/Cookie、endpoint pool、超时和 network/no-response 语义;原有 - accounts verify/rebind 测试通过。 -- [ ] OneApi/Veloera 适配器参数化测试证明 header、配额换算、模型 context cache、 - check-in 成功/失败语义一致。 -- [ ] 各 proxy log 路径通过结构化 owner 写入一次,字段、usageSource、billing、 - client/session/trace 元数据与现有契约一致,insert 失败仍按 best-effort - 规则处理。 -- [ ] proxy-core 不再新增或保留已解决的 routes/proxy import,Accounts 不再 - 直接 import 顶层 Tokens 页面;drift check tracked debt 数量下降。 -- [ ] server typecheck、相关服务/route/proxy tests、drift check、build 和 - diff check 通过;schema/migration 未被改动。 - -## 范围外 - -- 改变数据库 schema/migration/generated artifacts; -- 改变代理协议转换、路由选择或外部 API 合约; -- 无证据的“大一统 service”重写或删除兼容分支。 diff --git a/.trellis/tasks/08-31-server-boundary-simplification/task.json b/.trellis/tasks/08-31-server-boundary-simplification/task.json deleted file mode 100644 index 38f6681..0000000 --- a/.trellis/tasks/08-31-server-boundary-simplification/task.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "id": "server-boundary-simplification", - "name": "server-boundary-simplification", - "title": "服务端边界简化", - "description": "消除已证实的 service/route/proxy 重复并用聚焦测试清理边界债务。", - "status": "in_progress", - "dev_type": null, - "scope": null, - "package": null, - "priority": "P1", - "creator": "cita-777", - "assignee": "cita-777", - "createdAt": "2026-08-31", - "completedAt": null, - "branch": null, - "base_branch": "main", - "worktree_path": null, - "commit": null, - "pr_url": null, - "subtasks": [], - "children": [], - "parent": "08-31-project-wide-simplification", - "relatedFiles": [], - "notes": "", - "meta": {} -} \ No newline at end of file diff --git a/.trellis/tasks/archive/2026-08/00-bootstrap-guidelines/prd.md b/.trellis/tasks/archive/2026-08/00-bootstrap-guidelines/prd.md deleted file mode 100644 index 5e3da5e..0000000 --- a/.trellis/tasks/archive/2026-08/00-bootstrap-guidelines/prd.md +++ /dev/null @@ -1,149 +0,0 @@ -# Bootstrap Task: Fill Project Development Guidelines - -**You (the AI) are running this task. The developer does not read this file.** - -The developer just ran `trellis init` on this project for the first time. -`.trellis/` now exists with empty spec scaffolding, and this bootstrap task -exists under `.trellis/tasks/`. When they want to work on it, they should start -this task from a session that provides Trellis session identity. - -**Your job**: help them populate `.trellis/spec/` with the team's real -coding conventions. Every future AI session — this project's -`trellis-implement` and `trellis-check` sub-agents — auto-loads spec files -listed in per-task jsonl manifests. Empty spec = sub-agents write generic -code. Real spec = sub-agents match the team's actual patterns. - -Don't dump instructions. Open with a short greeting, figure out if the repo -has any existing convention docs (CLAUDE.md, .cursorrules, etc.), and drive -the rest conversationally. - ---- - -## Status (update the checkboxes as you complete each item) - -- [x] Fill backend guidelines -- [x] Fill frontend guidelines -- [x] Add code examples - -## Completion Evidence - -- Backend and frontend indexes now describe their pre-development and quality - checklists and link to the maintained guideline files. -- Guideline files cite real Metapi modules, tests, commands, and architectural - boundaries; template-only placeholder text was removed. -- The generated `.gitattributes` reference now points at the repository's actual - backend specification path. -- No product source files were changed as part of this bootstrap. - ---- - -## Spec files to populate - - -### Backend guidelines - -| File | What to document | -|------|------------------| -| `.trellis/spec/backend/directory-structure.md` | Where different file types go (routes, services, utils) | -| `.trellis/spec/backend/database-guidelines.md` | ORM, migrations, query patterns, naming conventions | -| `.trellis/spec/backend/error-handling.md` | How errors are caught, logged, and returned | -| `.trellis/spec/backend/logging-guidelines.md` | Log levels, format, what to log | -| `.trellis/spec/backend/quality-guidelines.md` | Code review standards, testing requirements | - - -### Frontend guidelines - -| File | What to document | -|------|------------------| -| `.trellis/spec/frontend/directory-structure.md` | Component/page/hook organization | -| `.trellis/spec/frontend/component-guidelines.md` | Component patterns, props conventions | -| `.trellis/spec/frontend/hook-guidelines.md` | Custom hook naming, patterns | -| `.trellis/spec/frontend/state-management.md` | State library, patterns, what goes where | -| `.trellis/spec/frontend/type-safety.md` | TypeScript conventions, type organization | -| `.trellis/spec/frontend/quality-guidelines.md` | Linting, testing, accessibility | - - -### Thinking guides (already populated) - -`.trellis/spec/guides/` contains general thinking guides pre-filled with -best practices. Customize only if something clearly doesn't fit this project. - ---- - -## How to fill the spec - -### Step 1: Import from existing convention files first (preferred) - -Search the repo for existing convention docs. If any exist, read them and -extract the relevant rules into the matching `.trellis/spec/` files — -usually much faster than documenting from scratch. - -| File / Directory | Tool | -|------|------| -| `CLAUDE.md` / `CLAUDE.local.md` | Claude Code | -| `AGENTS.md` | Codex / Claude Code / agent-compatible tools | -| `.cursorrules` | Cursor | -| `.cursor/rules/*.mdc` | Cursor (rules directory) | -| `.windsurfrules` | Windsurf | -| `.clinerules` | Cline | -| `.roomodes` | Roo Code | -| `.github/copilot-instructions.md` | GitHub Copilot | -| `.vscode/settings.json` → `github.copilot.chat.codeGeneration.instructions` | VS Code Copilot | -| `CONVENTIONS.md` / `.aider.conf.yml` | aider | -| `CONTRIBUTING.md` | General project conventions | -| `.editorconfig` | Editor formatting rules | - -### Step 2: Analyze the codebase for anything not covered by existing docs - -Scan real code to discover patterns. Before writing each spec file: -- Find 2-3 real examples of each pattern in the codebase. -- Reference real file paths (not hypothetical ones). -- Document anti-patterns the team clearly avoids. - -### Step 3: Document reality, not ideals - -**Critical**: write what the code *actually does*, not what it should do. -Sub-agents match the spec, so aspirational patterns that don't exist in the -codebase will cause sub-agents to write code that looks out of place. - -If the team has known tech debt, document the current state — improvement -is a separate conversation, not a bootstrap concern. - ---- - -## Quick explainer of the runtime (share when they ask "why do we need spec at all") - -- Every AI coding task spawns two sub-agents: `trellis-implement` (writes - code) and `trellis-check` (verifies quality). -- Each task has `implement.jsonl` / `check.jsonl` manifests listing which - spec files to load. -- The platform hook auto-injects those spec files + the task's `prd.md` - into every sub-agent prompt, so the sub-agent codes/reviews per team - conventions without anyone pasting them manually. -- Source of truth: `.trellis/spec/`. That's why filling it well now pays - off forever. - ---- - -## Completion - -When the developer confirms the checklist items above are done with real -examples (not placeholders), guide them to run: - -```bash -python3 ./.trellis/scripts/task.py finish -python3 ./.trellis/scripts/task.py archive 00-bootstrap-guidelines -``` - -After archive, every new developer who joins this project will get a -`00-join-` onboarding task instead of this bootstrap task. - ---- - -## Suggested opening line - -"Welcome to Trellis! Your init just set me up to help you fill the project -spec — a one-time setup so every future AI session follows the team's -conventions instead of writing generic code. Before we start, do you have -any existing convention docs (CLAUDE.md, .cursorrules, CONTRIBUTING.md, -etc.) I can pull from, or should I scan the codebase from scratch?" diff --git a/.trellis/tasks/archive/2026-08/00-bootstrap-guidelines/task.json b/.trellis/tasks/archive/2026-08/00-bootstrap-guidelines/task.json deleted file mode 100644 index ea32782..0000000 --- a/.trellis/tasks/archive/2026-08/00-bootstrap-guidelines/task.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "id": "00-bootstrap-guidelines", - "name": "00-bootstrap-guidelines", - "title": "Bootstrap Guidelines", - "description": "Fill in project development guidelines for AI agents", - "status": "completed", - "dev_type": "docs", - "scope": null, - "package": null, - "priority": "P1", - "creator": "cita-777", - "assignee": "cita-777", - "createdAt": "2026-08-31", - "completedAt": "2026-08-31", - "branch": null, - "base_branch": null, - "worktree_path": null, - "commit": null, - "pr_url": null, - "subtasks": [], - "children": [], - "parent": null, - "relatedFiles": [ - ".trellis/spec/backend/", - ".trellis/spec/frontend/" - ], - "notes": "First-time setup task created by trellis init (fullstack project)", - "meta": {} -} \ No newline at end of file diff --git a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/check.jsonl b/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/check.jsonl deleted file mode 100644 index 27e6781..0000000 --- a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/check.jsonl +++ /dev/null @@ -1,3 +0,0 @@ -{"file":".trellis/spec/frontend/component-guidelines.md","reason":"检查 focus、portal、aria 和组件复用"} -{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"检查测试、移动和构建"} -{"file":".trellis/spec/guides/documentation-language.md","reason":"检查新增任务说明是否遵循中文文档约定"} diff --git a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/design.md b/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/design.md deleted file mode 100644 index 8dfbaf7..0000000 --- a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/design.md +++ /dev/null @@ -1,47 +0,0 @@ -# 前端 UI 基础与 Radix 封装层:设计 - -## 组件分层 - -```text -page/domain component - ↓ semantic props -src/web/components/ui/* (Metapi-owned API + theme/classes) - ↓ behavior only -Radix primitives -``` - -第三方依赖只出现在 `components/ui`,并通过 CSS token 或现有 class 适配。 -业务页面不得知道 Radix 的 `Root/Trigger/Content` 结构。 - -## 兼容 API - -- `CenteredModal` 继续作为旧调用方 facade;内部可以委托新的 Dialog wrapper。 - 先补 role/aria-labelledby、focus return 和多实例 body-lock,再迁移调用方。 -- `MobileDrawer` 继续提供现有 `open/onClose/title/side` 语义;桌面 drawer - 与移动 drawer 共享底层 close/portal/focus 逻辑,但保留不同 CSS 尺寸。 -- `ModernSelect` 保留当前 `options`、`value`、`onChange`、搜索和 - placeholder props。Radix Select 只作为实现细节;若 Radix 的 test renderer - 或 portal 行为不兼容,先保持旧内部实现,不能破坏 facade。 -- `Button`/`AsyncButton` 只封装 `type`、`disabled`、`aria-busy`、loading - indicator 和现有 `.btn-*` variants,不把页面动作塞进一个 boolean 矩阵。 - -## 生命周期与无障碍 - -- 使用计数或 owner 集合管理 body scroll lock,避免两个 portal 同时存在时 - 一个卸载恢复滚动而另一个仍打开。 -- Dialog/Drawer 明确 close policy;默认行为保持现有调用方设置,不擅自把 - backdrop/Escape 从关闭变成开启。 -- 打开时记录触发元素,关闭时优先返回仍连接的触发元素,其次使用现有 - fallback focus;所有 close path 都清理 listener/timer。 -- Toast 使用 `role=status`/`aria-live`,卸载时清理 timeout,保留现有 toast - 去重与自动消失时间。 - -## 迁移顺序 - -1. 新增 wrapper 和测试,不动调用方; -2. 迁移低耦合的 Search/ChangeKey/settings modal; -3. 迁移下游/OAuth drawer 和 TokenRoutes confirm; -4. 保留 `ModernSelect` facade 后逐步切换内部行为; -5. 增量替换 Button/feedback,最后才考虑删除无用依赖或旧 CSS。 - -每一步都能单独回滚,且旧导出保持可用。 diff --git a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/implement.jsonl b/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/implement.jsonl deleted file mode 100644 index 6794d78..0000000 --- a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/implement.jsonl +++ /dev/null @@ -1,5 +0,0 @@ -{"file":".trellis/spec/frontend/component-guidelines.md","reason":"组件 props、Radix wrapper 和无障碍约束"} -{"file":".trellis/spec/frontend/quality-guidelines.md","reason":"前端测试和 drift gate"} -{"file":".trellis/spec/frontend/type-safety.md","reason":"facade 与第三方类型边界"} -{"file":".trellis/spec/guides/code-reuse-thinking-guide.md","reason":"避免重复 modal/select/button"} -{"file":".trellis/spec/guides/documentation-language.md","reason":"保持 Trellis 实施文档和清单使用简体中文"} diff --git a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/implement.md b/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/implement.md deleted file mode 100644 index db3f601..0000000 --- a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/implement.md +++ /dev/null @@ -1,35 +0,0 @@ -# 前端 UI 基础与 Radix 封装层:执行清单 - -## 实施步骤 - -1. 核对 package manager 和 React 18 peer 兼容性,只加入实际需要的 Radix - 包,保持 npm/pnpm lockfile 一致。 -2. 建立 `src/web/components/ui/` 目录和第三方 import architecture test。 -3. 提取共享 dialog/drawer 生命周期辅助逻辑,补多实例 body-lock、焦点返回、 - Escape/backdrop、portal fallback 测试。 -4. 让 `CenteredModal`、`MobileDrawer` 委托或复用新 wrapper,先确保旧测试通过。 -5. 迁移 SearchModal、ChangeKeyModal、settings modal、downstream/OAuth drawer - 和 TokenRoutes confirm/imperative dialog,逐个运行局部测试。 -6. 增加 `Button`/`AsyncButton`、统一 loading/empty/error primitive,迁移至少 - Accounts、Settings、Tokens 中的高频异步动作。 -7. 让 Toast 提供 aria-live 和 timer cleanup,保持 `useToast` 合约。 -8. 按 utility 搜索和 `build:web` 结果决定 Tailwind cleanup,并更新文档/guard。 - -## 重点验证 - -```bash -npm test -- src/web/components/centered-modal.test.tsx src/web/components/mobile-drawer.test.tsx src/web/components/ModernSelect.test.tsx -npm test -- src/web/pages/DownstreamKeys.test.tsx src/web/pages/settings.factory-reset.test.tsx src/web/pages/tokenRoutes.mobile-layout.test.tsx -npm run typecheck:web -npm run typecheck:web:test -npm run repo:drift-check -npm run build:web -git diff --check -``` - -## 停止条件 - -- Radix 改变 test renderer、focus、z-index、portal 或 controlled Select 语义且 - 无法通过 facade 兼容时,停止迁移并保留现有内部实现; -- 发现需要改协议/数据库或删除用户能力时,回到父任务评审; -- 不使用批量替换破坏用户已有 dirty/untracked 文件。 diff --git a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/prd.md b/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/prd.md deleted file mode 100644 index 3318dfd..0000000 --- a/.trellis/tasks/archive/2026-08/08-31-frontend-ui-foundation/prd.md +++ /dev/null @@ -1,59 +0,0 @@ -# 前端 UI 基础与 Radix 封装层 - -## 目标 - -采用 Iris 使用的组件边界:Radix 负责交互/无障碍原语,Metapi 自己的 -`components/ui` 负责稳定 props、主题和兼容性,页面只使用项目封装。降低 -重复弹窗、抽屉、选择器、按钮和反馈状态的维护成本,同时不改变现有页面能力。 - -## 已确认事实 - -- 当前没有 Radix 或完整 UI 套件依赖;`CenteredModal`、`MobileDrawer`、 - `ModernSelect` 是已有共享原语,但多个页面仍复制外壳。 -- `ChangeKeyModal`、`SearchModal`、多个 settings modal、下游/OAuth drawer - 绕过或复制共享壳;`TokenRoutes` 还有 imperative `innerHTML` dialog。 -- 生产代码约有 430 个原生 `