src/main/Electron main process (app lifecycle, native bridges, IPC handlers).src/preload/contextBridge layer for safe renderer APIs.src/renderer/Vue 3 UI, Pinia stores, styles, i18n assets.src/shared/cross-process utilities;src/types/shared TS types.rust_package/Rust N-API module, with tests inrust_package/__test__/.resources/app assets,build/packaging assets,vendor/bundled ffmpeg/chromaprint binaries,out/build output,docs/VitePress documentation site.
- 新增或修改
ipcMain.handle、ipcMain.on、ipcMain.handleOnce等主进程 IPC 通道时,必须同步检查src/preload/index.ts的安全白名单:exactInvokeChannels、exactSendChannels、exactOnChannels以及对应的前缀白名单。Renderer 只能调用已明确放行的通道。 - 不允许只注册主进程 handler 而遗漏 preload 白名单;这种遗漏会让 renderer 调用被安全层拦截,并且在未捕获 rejection 时表现为“点击没有反应”。
- 每次新增 IPC 通道后,交付前至少运行
npx vue-tsc --noEmit和pnpm run build,并用rg对照主进程注册名、preload 白名单名和 renderer 调用名;如果通道是动态拼接的,必须验证实际展开后的完整通道名仍被白名单覆盖。
pnpm installinstalls root dependencies.pnpm run devstarts the Electron + Vite dev workflow.pnpm run buildbuilds main/renderer intoout/.pnpm run build:win(orbuild:mac,build:linux) packages installers via electron-builder.- FRKB 不做 Linux 平台;
build:linux仅视为历史残留脚本,禁止将 Linux 作为正式支持、发布、验收或修复目标继续扩展。 pnpm run docs:devstarts VitePress dev server for documentation site.pnpm run docs:buildbuilds VitePress site intodocs/.vitepress/dist/.pnpm run docs:previewpreviews the built documentation site locally.pnpm run lintandpnpm run formatapply ESLint/Prettier rules.cd rust_packagethennapi build --platform --releasebuilds the native module (requires@napi-rs/cli).cd rust_packagethenyarn testrunsavatests.
- Indentation: 2 spaces; LF; trim trailing whitespace; end files with a newline.
- Prettier: single quotes, no semicolons,
printWidth: 100. - ESLint extends Vue 3 + Electron toolkit; component names may be single-word.
- 禁止编写非必要的
any/as any;优先使用明确类型,其次使用unknown配合运行时守卫,最后才允许保留极少数有充分理由的边界类型。 - 禁止用“兜底 / fallback / 回退到系统环境”代替真正修复;如需临时兜底,必须明确标注为临时措施,并继续修复根因后方可交付。
- Renderer UI 禁止使用原生 DOM
title属性做悬停提示;凡是按钮说明、状态说明、路径/文件名完整内容展示、文本省略后的悬停提示,统一使用项目现有bubbleBox.vue气泡框组件或基于它的明确封装。 - 上一条仅针对原生 DOM
title属性;自定义组件自身的titleprop 若不下沉为原生 DOM 属性,可以按组件设计继续使用,但禁止借此变相把提示文案重新落回原生title。 - 遇到
disabled按钮、图标按钮、输入框包装层等 hover 不稳定场景,也必须通过可悬停的外层锚点接入bubbleBox,禁止为了省事退回原生title。 - 新增或修改 Renderer UI 时必须同时适配并检查
theme-light和theme-dark;禁止只按暗色或只按亮色交付。颜色、背景、边框、阴影、伪元素、canvas 绘制、worker 绘制都应优先走主题变量。日间模式下所有波形区域必须使用统一灰色底色变量(如--waveform-bg/--waveform-bg-elev),避免 RGB 波形直接落在白底上。 - 单个代码文件不应超过 1100 行;超过 1100 行时必须进行拆分,且拆分后的模块放置位置与命名应符合现有项目结构与习惯。
- 临时运行时排查期间,如果仅因新增调试日志导致代码文件超过 1100 行,可以暂时不拆分;调试日志默认按 Debug Logging 规则在交付或提交前删除,删除后仍超过 1100 行才需要拆分。若用户明确要求把诊断日志提交上去,用于排查未来偶发问题,则允许保留并提交这类日志,但必须保持轻量、阈值化、目的明确。
- 不要在每次普通代码修改后都主动检查全量代码文件行数;只有准备提交、用户明确要求拆分/检查行数,或当前修改明显会让目标文件长期超过 1100 行时,才执行行数检查并按规则拆分。
- 如果用户提出“拆分代码”并指定了行数上限
N,则拆分后所有相关文件(包括原文件与新拆出的文件)都必须<= N行;若用户未指定,则默认执行<= 1100行规则。 - “拆分”必须为真拆分:禁止把主体逻辑整体挪到一个新文件后,仅在原文件保留单行转发/导入;原文件必须保留清晰且有实际价值的职责边界与实现。
- 拆分完成后必须执行行数校验并在交付时明确给出每个相关文件的实际行数,确保可核验。
- 后台核对、重扫、增量补丁绝不能让用户看见列表“退出重进”或不断闪动。刷新必须就地合并:等价的行复用旧对象引用(
src/shared/playlistViewMerge.ts的planSongListMerge),禁止用整份新列表覆盖originalSongInfoArr让 Vue 重建所有行。 - 后台刷新禁止重跑首次打开的渐进渲染、禁止重置滚动位置 / 选中项 / 当前播放行;内容完全没变化时必须一个 UI 动作都不做(连赋值都不做)。连续到达的多个刷新事件必须合并成一次(最新优先 + 单飞)。
- 行等价判定必须主进程与 renderer 共用
src/shared/songListItemCompare.ts;两边各写一套会导致反复推空补丁或漏刷新。 - 视图快照(
playlist_view_snapshot)只服务“读 / 展示”。移动、删除、改标签、写分析、导出 rekordbox XML、云同步对账等任何写操作都必须先走精确磁盘核对,禁止读快照。
- Rust module tests live in
rust_package/__test__/and follow*.spec.mjsnaming. - There is no root JS test runner yet; add new suites near the code they cover and document how to run them.
- 代码修改完成后必须运行
npx vue-tsc --noEmit;若存在错误,必须先修复再交付。
- 涉及运行时排查、交互链路排查、状态机排查时,默认把调试日志写入
log.txt可落盘链路,不要依赖浏览器控制台临时输出。 - 开发模式通过
pnpm run dev启动时,log.txt固定在当前项目根目录;只有打包运行时才读取 ElectronuserData目录下的log.txt,禁止混淆两者。 - Renderer 侧调试信息应通过现有 console bridge /
outputLog/ 主进程log体系进入log.txt,确保我复现一次后,Codex 可以自行读取日志继续排查。 - 禁止把“请把控制台日志复制给我”当成默认方案;除非日志链路本身损坏,否则应优先由 Codex 自己读取
log.txt。 - 常驻代码里禁止保留非报错落盘日志;提交前默认必须清理
log.info/log.warn/log.debug、普通console.log/console.info/console.warn/console.debug、以及各类 trace 型outputLog。默认只保留真正的错误/异常日志进入log.txt。若用户明确要求提交诊断日志用于排查未来偶发问题,可以保留对应日志,但必须说明触发条件、字段含义和后续清理条件,禁止把泛滥 trace 当成常驻日志提交。 - 只有在当前任务明确需要运行时排查时,才允许临时增加非错误调试日志;这类日志必须走现有
log.txt链路,并且默认在交付前删除,不能把调试噪音留在仓库里。用户明确要求保留并提交的诊断日志除外,但仍必须保持最小范围、可检索、可关闭或有明确阈值。 pnpm run dev启动阶段的开发提示、脚本提示、端口提示等输出,默认只打印终端,不写入log.txt。这类输出应使用开发脚本/终端控制台,不要接入主进程log或 rendereroutputLog落盘链路。
- 任何对外网络请求都必须默认遵守用户当前网络环境:用户开启 VPN / TUN 时应跟随系统路由;用户配置系统代理 / PAC 时必须显式接入系统代理,不能绕过。
- 主进程新增外部 HTTP 请求时,禁止直接写裸
fetch;必须复用现有系统代理链路(如getSystemProxy()+ProxyAgent或统一封装),确保请求自动走用户已开启的 VPN / 代理。 - Renderer 禁止直接发起新的外部网络请求;涉及外部服务统一下沉到主进程代理层处理,再由主进程按系统代理规则发出。
- Recent commits use Conventional Commit prefixes with optional scopes:
feat(ui): ...,fix(player): ...,refactor(...),docs(...). - 提交信息必须使用中文(保留 Conventional Commit 结构)。
- PRs should include a clear summary, testing notes, and screenshots for UI changes; link related issues and call out packaging or native-module impacts.
- 如果我说“发布预发布版本”,默认执行:更新
package.json版本号为X.Y.Z-rc.<当前时间>(时间格式YYYYMMDDHHmm),提交,并先 push main,再打同名 tag 并 push tag 触发发布流程。新 RC 的 GitHub Release 资产发布成功后,工作流自动删除此前所有应用 RC Release 与 tag(X.Y.Z-rc.*,不含 Demucs 资源标签);禁止在新 RC 发布成功前手工清理仍作为回退的上一版 RC。 - 如果当前版本是正式版(不含
-rc)且我要求预发布版本,自动把版本号 补丁位 +1(Z+1,逢 10 进 1,例如1.1.9 -> 1.2.0),再加-rc.<当前时间>并发布。 - 如果我说“发布正式版”,就把
-rc.以及后面的时间后缀去掉,仅保留X.Y.Z,提交,并先 push main,再打同名 tag 并 push tag 触发发布流程。 - 发布顺序固定:提交 → push main → 打 tag → push tag。
- Do not modify
README.mdorreadme/README_CN.mdwithout explicit maintainer approval.
- Copy
env.exampleto.envfor local config; never commit secrets. - If updating
vendor/binaries, ensureelectron-builder.ymlpackaging paths stay in sync.
- rekordbox样本预分拣:
scripts/move_rekordbox_playlist_grid_diffs.py,详细用法见drafts/rkb-benchmark-workflow/分拣脚本.md。- Python 环境:
vendor/demucs/win32-x64/runtime-cpu/python.exe(含 torch、soxr、beat_this 等 ML 依赖)。Rekordbox bridge 子进程会自动使用vendor/rekordbox-desktop-runtime/win32-x64/python/python.exe。 - 用户人工流程固定为
Upan -> test -> needReview -> review:艾特分拣脚本.md时从 Upan 取 500 首、 分拣后用户把 test + needReview 的本轮剩余项移入 review;艾特准备好rkb新样本.md时只从 review 的 report 绑定完整 roster 创建 consumed development batch。禁止绕过统一入口手工拼接 truth / benchmark / sealed 命令。 - 中央
rkb-dataset-registry.json必须先一次性覆盖历史 3745 个 consumed 样本,并具备不可变familyId、完整 Chromaprint、batchId和batchStatus = consumed;注册未完成时禁止 fresh prepare/evaluate,fresh/evaluating/exposed 记录禁止进入 development split。split / LOBO 必须先按固定纯音频 policy 合并为isolationFamilyId,禁止只按 exact family 声称无泄漏。 初次迁移固定按历史批次执行import-consumed,全部导入后执行initialize-registry --expected-track-count 3745,并用重复的--expected-batch <batchId>=<数量>锁死current1407 / blind608 / old377 / new357 / test327 / test353 / test316七批;rebuild-registry只用于初始化后的派生视图重建。 - FRKB 数据库根目录只认
.env的FRKB_BENCHMARK_DATABASE_ROOT/FRKB_DEV_DATABASE_URL; D 到 G 的文件迁移未完成时禁止切换配置、禁止按迁移中的目录瞬时数量初始化 registry,也禁止脚本自行猜盘符。 当前 FRKB-5 已切到G:/FRKB_database-E,历史 baseline 为 7 批 / 3745 首;禁止回退到已不存在的 D 盘目录。 - PCM / Chromaprint 身份计算默认使用
audio-identity-cache,按 16 首分块断点续算。缓存命中仍必须重算 asset SHA-256 并校验文件大小、mtime、helper SHA 和参数;缓存只能减少重复解码,不能代替身份校验。 - G 盘旧
.frkb_audio_library_manifest.json是 registry 建立前的 1916 首迁移快照,仅作历史记录; 当前数据权威源是rkb-dataset-registry.json+ baseline,禁止再拿旧 manifest 判断样本总数或根目录。 test差异分拣是 pre-review label QA:完整 roster、audio SHA-256、solver/hash 必须先写入 report, 再按同一 report 把差异项移至needReview。旧算法的这次比对不改变未来候选的证据资格;只有 review batch 被用于训练/调参后,才不能验证使用过它的同一候选。review prepare 必须按 report 绑定整批 roster 与音频身份;缺 fingerprint 或近重复时 fail closed。- 同一时刻只允许一个 fresh/evaluating/exposed 批次;存在活动批次或
sealed-intake除.frkb.uuid外仍有内容时,必须先 finalize/清理,禁止强行 prepare。.frkb.uuid是资料库节点身份标记,必须保留。 - 执行流程仍是先跑 dry-run;如果
errorTrackCount == 0,固定使用已生成报告的--from-report <report> --apply写回,不重新分析后直接 apply,也不需要再次询问用户。写回前必须校验 report 的 solver/snapshot 身份和 playlistrowKey。若用户要把本批纳入训练/调参,使用prepare --playlist review --reviewed-development --triage-report <report>直接 consumed;之后禁止根据本批 结果调参后再用本批证明同一候选提升。旧 consumed 数据维护才可传--consumed-maintenance。
- Python 环境:
- Upan 源头清理:
scripts/move_upan_non_integer_bpm_tracks.py,默认 dry-run;确认后加--apply。固定顺序:先从Upan直接移除 current truth 重复曲目和源歌单内部重复多余项,再将剩余曲目里 UI BPM(bridgebpm字段)非整数的移动到upanNonIntegerBpm。