Skip to content

admin 合并进 RB 仓 —— 实施计划

物理仓库位置:/Users/larry/reading-browser(本仓) 创建:2026-07-19 · 状态:✅ 已完成(2026-07-20 subtree 迁入,admin/ 现为本仓卫星子项目)

⚠️ 状态行此前一直写着"待执行(subtree 迁移未动手)",而 admin 早已合仓并持续开发—— 2026-08-14 文档治理时订正。后续项见 admin-integration-followups.md(亦已全部 ✅)。 关联记忆:project_readvocab_admin_and_shared_llm.md(生态=RB+RVH+readVocab-admin+共享 LLM 层)


0. 一句话

~/readVocab-admin(Next.js 运营后台)以 git subtree 方式迁入本仓 admin/ 子目录,让它和它本就依赖的 supabase/(同一个 Supabase 项目的 sql + edge functions)团聚,消除跨仓手抄 schema 的静默漂移,实现"一个会话里改后台 + edge function + schema"。RVH 不合并(异构 Flutter/Dart,合 git 省不掉隔离会话)。


1. 决策依据(调查证据,2026-07-19)

admin 仓事实(~/readVocab-admin,git remote ttfishnet/readvocab-admin):

  • 技术栈是 RB 前端的孪生:Next.js 16.2.3(App Router)+ React 19.2 + TS strict + pnpm + Tailwind 4 + shadcn + cva + lucide + recharts。规模 58 个 .ts/.tsx、~7,855 LOC。
  • 耦合对象 = Supabase 后端层,不是 RB 桌面端
    • 数据访问 100% 走 @supabase/supabase-js(无 pg/Prisma/Drizzle)。
    • 两个 client:lib/supabase-client.ts(anon,仅 Auth)+ lib/supabase-server.tsservice_role,绕 RLS,全库管理员权限lib/queries.ts 每个函数都用它)。
    • 读写 RB 拥有的表:recommended_sites/feeds/articlesvocabulary、治理 5 表(llm_call_log / edge_run_log / recommend_audit_log / recommended_blocklist / recommend_config)、用户分析聚合(user_notebook_entries / user_reading_sources)。RPC:get_user_count / get_registration_trend
    • 调用 RB 的 edge functionapp/actions/recommend-config.ts 硬编码 PROJECT_REF="jdtbyteiwnciqnfppztz"POST /functions/v1/{discover-sites,analyze-articles}(这俩函数就在本仓 supabase/functions/)。
    • 与 RB 桌面源码零 cross-import(不同框架,无共享前端代码)。
  • 同一个 Supabase 项目实锤:本仓 supabase/.temp/linked-project.jsonref = admin .env.local URL 的 ref = jdtbyteiwnciqnfppztz。同库、同 edge function。
  • 类型是手抄镜像且已漂移:admin 的 TS 接口是本仓 supabase/sql/* 列清单的手工复制;RB 2026-07 把 user_reading_sources 改名 user_reading_pages,admin 至今仍查旧名 lib/queries.ts:474——跨仓契约已静默断裂。这是分仓的活代价,也是合仓最硬的理由。
  • 部署:Vercel(.vercel/project.jsonprj_zm1tTpSl0eJ3p7FC4MJklkGcaeUQ / readvocab-admin)。无 CI(无 .github/)。

安全预检结论(2026-07-19,已核)

  • .env.local(含真实 service_role key)从未进 git history、未被跟踪.gitignore 已盖 .env* + .vercel
  • history 里的 service_role 匹配均为环境变量名引用,非密钥值。→ 无需轮换密钥。合仓时保持 .env.local 不入库即可(本仓 .gitignore 同样盖 .env*)。

2. 目标结构(渐进式,先不上 workspace)

reading-browser/
├── src/  src-tauri/          # 桌面客户端(不动)
├── supabase/                 # sql + functions + _shared(admin 的契约源头)
├── admin/                    # ← 迁入:自包含 Next.js 应用
│   ├── package.json          # 自己的依赖 + 自己的 node_modules
│   ├── CLAUDE.md             # admin 专属指令(嵌套,Claude 在 admin/ 工作时自动加载)
│   ├── .env.local            # 不入库,Vercel Dashboard 配置
│   └── ...(app / components / lib / docs 原样迁入)
├── docs/  .claude/  ...      # RB 现有(skill 需排除 admin/,见 §3.4)

为什么先不上 pnpm workspace:admin 与 RB 目前无共享 npm 包,强行 workspace 只增加 hoisting/依赖协调成本(Next16 只 admin 用)。等真要抽 @readbrowser/db-types 共享类型包时(§5)再加 workspace。符合渐进式偏好。

部署管线保持三条独立(合 git 不合部署):

  • RB 桌面:Tauri build / GitHub Actions(Windows)
  • Supabase:/edge-deploy skill
  • admin:Vercel(Root Directory 指向 admin/

3. 迁移步骤

3.1 subtree 迁入(保留 admin 提交历史)

bash
cd /Users/larry/reading-browser
git subtree add --prefix=admin git@github.com:ttfishnet/readvocab-admin.git main
  • 保留 admin 全部 commit history(勿 copy 文件丢历史)。
  • 迁入后 admin/node_modulesadmin/.nextadmin/.vercel 应被忽略:确认本仓 .gitignore 覆盖或在 admin/.gitignore 保留。

3.2 老仓收尾

  • ttfishnet/readvocab-admin 转归档/镜像,停止往它推。
  • 后续开发只在本仓 admin/ 进行。

3.3 Vercel 重连

  • Vercel 项目 readvocab-admin 的 Git 源改到 reading-browser 仓。
  • Root Directory 设为 admin/(Vercel 原生支持 monorepo 子目录)。
  • 环境变量(NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY / SUPABASE_SERVICE_ROLE_KEY / ADMIN_EMAILS)已在 Vercel Dashboard,无需改动;确认构建触发条件(可设为仅 admin/** 变更时构建,避免 RB 改动触发无谓部署)。

3.4 Skill 作用域收敛(关键)

RB 的 /arch-check/build-check/code-review/ui-check 按 RB 不变式写(hex 字面量、ListItem、invoke 收敛、cargo/pnpm build 等),会把 admin/ 的 Next.js 代码误判违规。

  • 让这些 skill 的扫描/构建范围排除 admin/(改各 SKILL.md 的目标路径或 glob)。
  • admin 用它自己的 eslint-config-next lint,不套 RB skill。
  • admin 若有自己的 skill/.claude/,随目录迁入 admin/.claude/

3.5 记忆与文档

  • admin 独立 Claude 项目记忆会变孤儿:把有用条目迁进 RB 项目记忆(~/.claude/projects/-Users-larry-reading-browser/memory/)。
  • admin 的 CLAUDE.md(注意:其 prose 说 "Next.js 15" 是 stale,实际 16.2.3——迁入时顺手修)作为 admin/CLAUDE.md 保留;不要把 admin 内容塞进本仓根 CLAUDE.md(已很大)。
  • admin 的 docs/(architecture.md / database.md / plans)迁到 admin/docs/

4. 合并后立即兑现的修复

  • [ ] 修活漂移admin/lib/queries.ts:474.from("user_reading_sources")user_reading_pages(RB 2026-07 已改名,见 supabase/sql/rename-tables-2026-07.sql)。合仓后一个会话即可核出全部旧表名引用。
  • [ ] 顺手核对 admin 其余手抄接口 vs supabase/sql/* 当前列集合,扫一遍别的漂移(如 user_notebook_entriesuser_learning_entries 改名是否波及)。
  • [ ] 修 admin/CLAUDE.md 的 Next.js 版本笔误(15 → 16.2.3)。
  • [ ] 补 admin CI(可选):迁入后可复用本仓 GitHub Actions 加 admin 的 typecheck/lint job,或继续依赖 Vercel 构建门。

5. 中期收益(触发时再做,非本次必做)

单一 schema 真相源,根除手抄漂移:

  • 从 Supabase 生成 database.types.tssupabase gen types typescript)。
  • admin lib/queries.ts 的手抄接口改为 import 生成类型;supabase/sql 改动 → 重生成类型 → admin 编译期报错。
  • 若 RB 前端也想共享该类型,此时抽 packages/db-types + 引入 pnpm workspace(§2 推迟的那一步的真正触发点)。

6. RVH 明确排除(结论不变)

RVH(~/reading_vocab_helper,Flutter/Dart)不合并

  • 与 RB 桌面源码异构,合 git 省不掉你主动想要的 Dart/Rust 上下文隔离(CLAUDE.md §9 会话隔离规则)。
  • 跨端协调继续走交接文档 + 绝对路径引用;临时对照代码用会话 --add-dir ~/reading_vocab_helper 只读挂载,不合仓。

7. 回滚方案

  • subtree 迁入是一个 merge commit;若决定放弃,git revert 该 commit 即可移除 admin/,老仓 ttfishnet/readvocab-admin 保持完好未删。
  • Vercel 重连前先不动老仓部署,确认新仓构建通过再切源。

8. 执行清单(勾选推进)

  • [x] §3.1 git subtree add --prefix=admin ~/readVocab-admin main —— 2026-07-19 完成,commit 1d5a3e2(从本地路径迁入,86 文件,保留来源引用;WIP 已 stash/pop 无冲突)
  • [x] 确认 .gitignore 覆盖 admin/{node_modules,.next,.vercel} + admin/.env* —— admin 自带 .gitignore 随迁入生效 + RB 根 node_modules 规则,五类产物全 IGNORED,无需改动
  • [x] §3.4 skill 作用域排除 admin/ —— 2026-07-19 完成。审后发现只有 2 个真会误伤:code-review(增量收集处加 grep -v '^admin/')+ build-check(加 admin 独立 tsc --noEmit 检查分支)。arch-check / ui-check 全部 rg 硬绑 src/·src-tauri/,天然隔离,仅各加一行"作用域=RB、勿写根级 glob"防御性说明。(潜在长尾:doc-sync-check 是 git-diff 驱动,admin 改动可能误映到 RB 文档——低危,待它真误触时再收敛)
  • [x] §4 修 admin/lib/queries.ts 漂移 —— 2026-07-19 完成,live Supabase 探针已定案rb_supabase_query vocabulary 确认:无 id 列、PK=word、有 primary_cefr_level/cefr_inferred/cefr_source/word_tags)。改动:
    • 表名user_notebook_entriesuser_learning_entries(×7) + user_reading_sourcesuser_reading_pages(×1)。
    • learning_entries 列.select("vocabulary_id").select("word")(getTopWords/getLearnerCefr,此前双重失效);getLearnerCefr vocabulary join .in("id").in("word")
    • vocabulary id→word 整链重写(live 判定无 id 列后执行):VocabWordid 字段 + 文档串更新;getVocabStats count 查询 .select("id")wordsearchVocabulary/getVocabularyPage select 列去 id,updateWordCefr(id)(word) .eq("word")batchUpdateCefr(ids)(words) .in("word")app/actions/vocabulary.ts 参数改名;vocab-manager.tsx 选择态 selectedIdsselectedWordsw.id/editingWord.id.word、行 key 改 word。
    • 保留正确未动recommended_sites/feeds/blocklist + recommend_config(id=1).eq/.in("id")——SERIAL/单行主键表,确有 id。
    • 验证cd admin && pnpm install + pnpm exec tsc --noEmit → exit 0。
    • 另记一处独立隐患(未修,非本次 id 漂移)app/admin/(dashboard)/vocabulary/page.tsx summarizeWords 按旧 pos_definitions 形状([{pos,def_en,def_zh}])JSON.parse,但 live 是 JSONB 对象({noun:[{cefr,definitions:[{gloss,examples}]}]})——try/catch 兜底降级不崩,但预览渲染不对。归入 ③/后续单独治理。
  • [x] §3.5 记忆/docs/CLAUDE.md 归位 —— 2026-07-19 完成。admin/CLAUDE.md:合仓头注 + Next.js 15→16.2.3(×2) + §2 路径/DDL 源路径(../supabase/sql/sync-tables.sql) + §7 用户同步表旧名全表→当前 10 张 + vocabulary "PK=id UUID/94K"→"PK=word 无 id 列/缓冲池"(今天 bug 的文档根源)。记忆:更新 project_readvocab_admin_and_shared_llm.md("跨仓/独立新会话"断言→合仓现实)+ MEMORY.md 指针。
  • [x] 更新本仓根 CLAUDE.md —— 2026-07-19 完成。§2 目录树加 admin/ 行 + §9 新增「admin 子项目(2026-07 合仓)」小节(合仓理由 + skill 边界 + 改前核对 schema 提示)。
    • 遗留低优(未修,非误导性):admin/docs/database.md 可能仍记 vocabulary id UUID;admin/CLAUDE.md co-author 行「Opus 4.6」+ §2 树头 readvocab-admin/。自然触达时顺手。
  • [x] 运行时验证(2026-07-20)—— pnpm --dir admin dev(补 .env.local 后)起 :3000,SSR 命中 /admin/vocabulary + /admin/analyticsid→word 重写 + 表名修复对 live 生效(无 id 列/旧表名/Failed to fetch 报错)。验证中抓出并修掉一处真 bug——summarizeWords 对 jsonb 对象形 .slice 崩溃(commit 711498a)。commit c267592(合仓收尾)+ 711498a 已推 GitHub+Gitee。
  • [x] §3.3 Vercel 重连 —— 用户 2026-07-20 完成
  • [x] §3.2 老仓归档 —— 用户 2026-07-20 完成

全部完成 ✅ —— admin 合仓(代码 / schema 漂移 / skill 隔离 / 文档 / 记忆 / 推送 / Vercel 重连 / 老仓归档 / 运行时验证)闭环。