主题
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.ts(service_role,绕 RLS,全库管理员权限,lib/queries.ts每个函数都用它)。 - 读写 RB 拥有的表:
recommended_sites/feeds/articles、vocabulary、治理 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 function:
app/actions/recommend-config.ts硬编码PROJECT_REF="jdtbyteiwnciqnfppztz",POST /functions/v1/{discover-sites,analyze-articles}(这俩函数就在本仓supabase/functions/)。 - 与 RB 桌面源码零 cross-import(不同框架,无共享前端代码)。
- 数据访问 100% 走
- 同一个 Supabase 项目实锤:本仓
supabase/.temp/linked-project.json的ref= admin.env.localURL 的 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.json→prj_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-deployskill - 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_modules、admin/.next、admin/.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-nextlint,不套 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_entries→user_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.ts(supabase 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 完成,commit1d5a3e2(从本地路径迁入,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_entries→user_learning_entries(×7) +user_reading_sources→user_reading_pages(×1)。 - learning_entries 列:
.select("vocabulary_id")→.select("word")(getTopWords/getLearnerCefr,此前双重失效);getLearnerCefr vocabulary join.in("id")→.in("word")。 - vocabulary id→word 整链重写(live 判定无 id 列后执行):
VocabWord去id字段 + 文档串更新;getVocabStatscount 查询.select("id")→word;searchVocabulary/getVocabularyPageselect 列去id,;updateWordCefr(id)→(word).eq("word");batchUpdateCefr(ids)→(words).in("word");app/actions/vocabulary.ts参数改名;vocab-manager.tsx选择态selectedIds→selectedWords、w.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.tsxsummarizeWords按旧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.mdco-author 行「Opus 4.6」+ §2 树头readvocab-admin/。自然触达时顺手。
- 遗留低优(未修,非误导性):
- [x] 运行时验证(2026-07-20)——
pnpm --dir admin dev(补.env.local后)起 :3000,SSR 命中/admin/vocabulary+/admin/analytics:id→word 重写 + 表名修复对 live 生效(无 id 列/旧表名/Failed to fetch 报错)。验证中抓出并修掉一处真 bug——summarizeWords对 jsonb 对象形.slice崩溃(commit711498a)。commitc267592(合仓收尾)+711498a已推 GitHub+Gitee。 - [x] §3.3 Vercel 重连 —— 用户 2026-07-20 完成
- [x] §3.2 老仓归档 —— 用户 2026-07-20 完成
全部完成 ✅ —— admin 合仓(代码 / schema 漂移 / skill 隔离 / 文档 / 记忆 / 推送 / Vercel 重连 / 老仓归档 / 运行时验证)闭环。