Repository navigation
RinCore v5.3.49
RinCore v5.3.49
★★★彻底维修:把模式判定从「Compose 组合期间」移出来(工作模式行为零变化,数据面零接触)🔧这一版只改 2 个文件、39 行,但修的是一个会真实拖慢首帧的反模式;并且把「至少工作模式能用」这件事逐条证到了底。 ━━ 一、修的是什么(真问题)━━ v5.3.48 我把模式判定与自愈写在了 setContent { remember { ... } } 里: remember 的 block 是在 Compose 组合期间执行的 —— 而它里面既有 SharedPreferences 读,又可能触发 commit()(同步磁盘写)。在组合期间做磁盘 IO 是 Compose 明确的反模式:会拖慢首帧,且 remember 的 block 在组合被取消/重试时语义不保证。 现在:自愈与判定移到 onCreate 里、setContent 之前,作为普通 Kotlin 代码执行一次;组合侧只拿一个纯值 remember { mutableStateOf(initialMode) }。组合期间零 IO。 ━━ 二、验证(5 个场景 / 14 项断言,编译真实源码实跑)━━ 用 kotlinc 编译真实的 AppMode.kt(补内存版 SharedPreferences + Log stub),并在验证程序里写出与 RouteActivity.resolveInitialMode 完全同构的判定函数,跑: ①全新安装 → 工作模式,且零写盘(证明不会给新用户留下任何状态)/②上次停在 WORK → 仍是 WORK,零写盘(证明日常用户完全无感)/③上次停在 CHAT 且正常渲染过 → 保持 CHAT,零写盘(尊重用户选择)/④上次停在 CHAT 但没起来 → 自动回退 WORK 并落盘,回退后哨兵清除,二次启动零写入(证明自愈只发生一次、不会每启动一次就写盘)/⑤连续 5 次判定结果一致(幂等)。14/14 全过。 ━━ 三、「至少工作模式能用」—— 这是本轮最重要的部分 ━━ 工作模式的代码路径与上一版完全一致:RenderRequestHost() + AppRoutes() 一字未改,只是外面包了一层 when。新增开销只有一次 SharedPreferences 读(毫秒级)+ 一次枚举比较。 三层兜底,每一层都验过:①哨兵自愈(切到 CHAT 没起来 → 下次回工作模式并落盘);②安全模式重置(崩溃后进安全模式时强制写 WORK,避免「CHAT 崩 → 安全模式 → 退出 → 又进 CHAT → 再崩」死循环);③默认值兜底(读不到/脏数据/大小写变体一律回落 WORK)。三者合起来保证:任何情况下都至少能进工作模式。 ━━ 四、数据安全(逐项核验为 0)━━ 全库 grep AppMode 与数据面的交集:涉及数据库 0 处 / 涉及工作区或文件 0 处 / 涉及记忆 0 处。模式切换只写 rincore_shell 这个独立 SharedPreferences 的两个键。安装这一版不会动你已有的对话、设置、记忆、工作区、rootfs、附件。 ━━ 五、顺带修的一处静默 ━━ AppModeStore.write() 原本忽略 commit() 的返回值 —— 落盘失败(磁盘满/权限)时,用户界面上像是切了、重启却回到旧模式,且没有任何线索。现在返回 Boolean 并在失败时打 AppModeStore 日志。失败不会破坏已有数据(SharedPreferences 写入是原子的),所以只记录、不中断。 ━━ 六、诚实边界 ━━ ①无安装实测(沙箱无设备),以 14 项实跑 + tree-sitter 双文件 PASS + rc check 双 PASS + audit fast AUDIT-OK 为据;②聊天模式仍是占位页(Mika 代码未接入),所以「CHAT 崩溃」这条路径目前只在机制上就位、不会被真实触发;③切换模式会中断正在进行的回复生成(recreate() 结束当前屏幕协程),已落库内容不丢,但当前这一轮最后一段可能收不全 —— 建议等这一轮结束再切;④本轮零新增功能,严格限定「修组合期间副作用 + 补可观测性」。 ★版本纪律:5.3.48 → 5.3.49(只改第三位、不进位)。 ⑩hub=v5.3.49
本次提交
- edb2469 release: v5.3.48 — ★★★模式切换的数据安全与自愈:不会碰你任何数据,聊天模式起不来会自动退回工作模式,上次在哪个模式关下次还在那个模式🔒这一版不加功能,只做三件事:证明它不碰数据、给它兜底、把「上次在哪个模式」钉死。 ━━ 一、先回答你最担心的:装新版会不会把之前的东西搞坏 ━━ 模式切换只写一个东西:
rincore_shell这个独立的 SharedPreferences 里两个键(app_mode字符串 +chat_mode_pending布尔)。它不碰:数据库、聊天记录、设置、记忆、工作区、rootfs、上传附件。 我逐条核验了切换路径 ——RouteActivity与ChatDrawer里全部代码只有AppModeStore.write(...)+recreate(),没有任何一行数据库或文件写入;并且全库 grepAppMode与workspace/filesDir/rootfs/memory的交集为 0。 为什么能这么确定:AppMode是壳层的、属于「这个应用现在显示哪个界面」,跟「应用里存了什么」在代码上是两条完全不相交的路径。切模式不迁移、不清洗、不改写任何既有数据。 ━━ 二、你说「上次在哪个模式关闭,开了之后要还是在那个模式」—— 这条之前是错的 ━━ 原实现用的是apply()(异步落盘)。它的问题正是:用户刚切完模式、还没落盘就被系统杀进程(后台清理非常常见)→ 下次打开还是旧模式。你描述的「上次在一个模式关闭的,开了之后又到另一个模式了」,机制上完全成立。 现在改commit()(同步写):一次切换只写两个键,毫秒级代价,换来确定性 —— 切完立刻生效,杀进程也不丢。 ━━ 三、一个模式挂了绝不能让整个软件打不开(新增两层兜底)━━ 风险场景(必须有答案):你切到聊天模式 → 关掉应用 → 如果聊天模式这一侧起不来(Mika 初始化失败、渲染崩溃……)→ 下次打开直接崩 → 你再也进不去,连工作模式都用不了。这是绝不能接受的。 ① 哨兵自愈:进入聊天模式时置一个「未成功」标记,聊天模式成功渲染后清除。下次启动如果发现标记还在 = 上一次没跑起来 → 自动退回工作模式并落盘。宁可回工作模式(至少能用),也不把你关在一个起不来的界面里。 ② 安全模式重置:走到安全模式说明刚崩过 → 里面强制把模式重置为工作模式。否则会出现这条死循环:聊天模式崩溃 → 安全模式 → 退出 → 又进聊天模式 → 再崩。 工作模式本身不受影响:它是默认值、是自愈目标、是安全模式的落点 —— 三重保证下它一定进得去。 ━━ 四、可用性:切回来不会「回到首页」━━ 占位页的「返回工作模式」按钮我原本只改状态不重建 Activity —— 那会让工作模式的导航栈丢失(你切过去再切回来,会发现回到了首页,而不是原来那个对话)。现在统一为与抽屉按钮完全相同的路径:写盘 +recreate()。AppRoutes用的是rememberNavBackStack(startScreen)(navigation3 的 saveable 实现),recreate()带savedInstanceState重建时会恢复原来的对话位置。 ━━ 五、验证(16 项,用真实AppMode.kt编译后实跑)━━ 首次安装默认工作 / 切聊天后能读到聊天 / 写入是同步 commit / 切换后哨兵置位 / 成功后哨兵清除 / 正常重启仍是聊天(尊重你的选择) / 未成功时哨兵仍在 / 起不来自动回退工作 / 回退结果已落盘 / 回退后哨兵清除 / 反复切换以最后一次为准 / 脏数据或大小写变体回落工作 / 小写 chat 能识别 / 安全模式后回工作 / 安全模式后哨兵清除 —— 16/16 全过。 过程中我自己的断言写错一条(repeat(5)的末次是偶数,我写成了 WORK,实际是 CHAT)—— 实跑立刻暴露,说明这套验证是真的在验证而不是走过场。 ━━ 六、必须提前告诉你的一个行为 ━━ 切换模式会中断正在进行的回复生成(recreate()会结束当前屏幕的协程)。已经落库的内容不会丢(流式落库是原子写),但当前这一轮还没落库的最后一段可能收不全。建议:等这一轮回复结束再切换。若你要我做「正在生成时切换先弹确认」,说一声,可以加。 ━━ 七、诚实边界 ━━ ①聊天模式现在仍是占位页(Mika 代码还没接入),所以「聊天模式崩溃」这一侧目前很难被真实触发 —— 但机制现在就在,等 Mika 接进来那天它立刻生效;②无安装实测(沙箱无设备),以 16 项实跑 + tree-sitter 三文件 PASS +rc check双 PASS 为据;③切模式不清理任何缓存/数据,只是换一套界面;④没有做「模式切换前自动保存草稿」的额外动作 —— 草稿本来就在ChatVM.onCleared里保存(v5.3.18 幂等判据保证不会写坏)。 ★版本纪律:5.3.47 → 5.3.48(只改第三位、不进位)。 ⑩hub=v5.3.48 - e0b07c9 release: v5.3.47 — ★★双模式外壳落地:抽屉底部一个按钮,工作(RinCore)与聊天(Mika)是同一个 APK 里两个完全独立的 app🔄这一版按你的定案做:不做代码合并,只把两个应用放进同一个 APK,各走各的。 ━━ 一、为什么必须是同一个 APK(技术结论,不是偏好)━━ 你说「两个应用唯一的共同点是寄宿在同一个软件里、共用同一个 Linux 沙箱」—— 这句话在 Android 上直接排除了「两个独立 APK 互相跳转」:应用私有目录按包名隔离,
me.rincore.app和com.wangzan101.mika各自有自己的files/workspaces/,谁也读不到谁的 proot rootfs。双 APK 方案满足不了「共用同一个沙箱」这条硬要求。 所以:同一个 APK + 两套完全独立的运行时 + 一个共享的外壳。这也正是你说的形态。 ━━ 二、本版实际做了什么 ━━ ① 模式状态(壳层,独立于两边) —— 新建data/appmode/AppMode.kt:AppMode { WORK, CHAT }+AppModeStore(读/写/切换)。刻意用 SharedPreferences 而不是 DataStore:要在RouteActivity.onCreate之前同步读到一个字符串,DataStore 是异步的、首帧拿不到值会先渲染错模式再闪一下。也刻意不放进 RinCore 的 Settings —— 那是工作模式自己的数据,壳层状态寄存在里面就等于两个应用又开始纠缠。 ② 抽屉底部切换按钮 —— 你描述的位置:左侧「用量查询 / 收藏 / 统计数据」三个图标,右侧设置。现在在两者之间加了一个「聊天」按钮(BubbleChat图标)。点击 = 写壳层偏好 +recreate(),让RouteActivity重读模式后整棵 UI 树换掉 —— 这样两边不可能因为共享状态而互相影响,正是你要的隔离。 ③ 聊天模式入口 ——ChatModeEntry:当前是明确的占位页,不是假界面。一个假的聊天页只会让人以为已经接上了;这里直接写清「外壳与切换已就位,Mika 业务代码尚未接入」。页面上有「返回工作模式」,两端都能切。 ━━ 三、接入 Mika 时要守的五条边界(已写进代码注释,防将来不知不觉又合并)━━ ①各自的依赖容器(RinCore 的 Koin 与 Mika 的容器互不可见);②各自的数据库(不同 db 文件名,绝不共表);③各自的偏好(Mika 不读 RinCore 的 DataStore,反之亦然);④各自的技能 / 对话 / MCP 服务器(两边是两套独立数据);⑤唯一共享:files/workspaces/(Linux 沙箱)与这个外壳 APK。 ━━ 四、必须提前说清的一件事(不是合并,但工作量在)━━ Mika v0.1.35 用 Compose BOM 2024.12.01,我们用 2026.09.00。同一个 APK 里 Compose 运行时只能有一个版本 —— 这不是「要不要融合代码」的选择题,而是「能不能装进同一个 APK」的物理限制。 所以接入 Mika 时必须做的是:把 Mika 的界面代码适配到我们的构建环境(不是改它的功能、不是融合两边的业务逻辑,只是让它能在新版本 Compose 上编译)。好消息是 Mika 的core模块(707 个 .kt)是纯 JVM、零 Compose、零 Android 依赖,可以直接搬;只有app里 77 个 UI 文件需要逐个适配。 这才是这条路上唯一真正的硬骨头,我会分阶段做,每阶段独立可验证、失败可整段回退。 ━━ 五、诚实边界 ━━ ①本版没有 Mika 的任何代码 —— 只有外壳、切换、状态持久化与占位页。我没有假装它已经接上了。②**「聊天」按钮切换后看到的是占位页**,不是聊天界面;点「返回工作模式」可立即切回。③无安装实测(沙箱无设备),以 tree-sitter 四文件 PASS +rc check双 PASS 为据。④版本号按你的规则走第三位(5.3.46 → 5.3.47),没有因为「功能维度变了」就进 5.4.0。 ★版本纪律:5.3.46 → 5.3.47(只改第三位、不进位)。 ⑩hub=v5.3.47 - bb052f3 release: v5.3.47 — ★★记忆工具摘要不再显示路径,改为显示记忆正文;并为 Mika 合并完成可行性取证📖🧠━━ 一、记忆工具摘要:路径 → 正文 ━━ 你说的那句「显示读取的记忆,然后下面是内容,但这个内容它为什么现在是路径?我看不懂,也不重要」,我核对到了唯一一处:
MemoryFileToolUI.Summary里写的就是text = path。 现在正文来源按优先级取:①参数里的content(write / append)、new_str/old_str(str_replace)→ 这是模型这次真正写进去的文字;②输出里的files[0].content(memory_read)→ 这才是「读取到的记忆」;③都没有(delete / list)→ 不显示摘要(标题已经写明「删除记忆」/「列出记忆」,不再堆一串路径)。 配套两个细节:正文会先剥掉 frontmatter(---开头那段title:/kind:是给模型看的元数据,你要读的是记忆内容);正文可点击,点一下仍能打开该记忆的详情页——路径还在内部用来定位,只是不再出现在界面上。 验证:stripFrontmatter抽出真实函数用 kotlinc 编译,4 个用例全过(正常 frontmatter / 空正文 / 无 frontmatter 原样 / 非法的---不误剥);tree-sitter +rc check双 PASS。 ━━ 二、Mika 合并:先给你可执行的结论,不画饼 ━━ 我拉了 Mika 最新 tagv0.1.35的完整源码(不是凭记忆),逐项核对了技术栈: | | RinCore | Mika v0.1.35 | |---|---|---| | Kotlin | 2.4.21 | 2.1.20 | | AGP | 9.4.0 | 8.7.3 | | Compose BOM | 2026.09.00 | 2024.12.01 | | 模块 | 多模块 |:core+:app| | 包名 |me.rincore.app|com.wangzan101.mika| | .kt | 1353 | 1355 | 关键好消息(这是决定性的):Mika 的core模块(707 个 .kt)是纯 JVM 模块 ——plugins { kotlin.jvm },零 Compose、零 Android 依赖,只用到 kotlinx-serialization / coroutines / okhttp / protobuf-javalite / commons-compress。这些我们的项目全都有,版本也兼容。 所以合并分三层难度:①core707 文件 = 直接可搬(加一个:mika-core模块,用我们的 Kotlin 编译即可);②app里 262 个文件无 Compose 依赖 = 可搬(可能有 Android API 版本差异,需逐个修);③app里 77 个 UI 文件 = 唯一真正的硬骨头 —— 两年跨度的 Compose API 变更,必须逐个升级。 这一点必须说清:同一个 APK 里 Compose runtime 只能有一个版本,所以不能「Mika 用旧的、我们用新的」。要么升 Mika 的 UI,要么放弃 UI 层复用。 为什么不建议两个 APK 互相跳转:你说过「唯一共同点是寄宿在同一个软件里、共用同一个 Linux 沙箱」——而 Android 的私有目录按包名隔离,两个 APK 根本没法共用同一个 proot rootfs。双 APK 方案满足不了你提的核心要求,所以只能走单 APK。 躺在我这边的判断:core先落地是零风险高收益的一步——它能立刻验证「Mika 的 agent 内核能不能在我们的构建体系里编译通过」,而这个数字(编译错误条数)是后续所有工作量的基准。我下一步就是把它加成一个独立模块并推到 CI 拿这个数字,不污染现有功能、失败可整模块删除。 ━━ 三、诚实边界 ━━ ①记忆工具摘要改动只影响显示,工具参数、返回值、跳转行为未动;②Mika 合并本轮只完成取证与技术栈核对,没有任何源码合入(不想在没数据前给你画饼);③无安装实测(沙箱无设备),以 kotlinc 实跑 4 项 + tree-sitter +rc check为据;④Mika v0.1.35 是 tag 而非 release(该仓库无 GitHub Release),我用的是 tag 对应的 commit。 ★版本纪律:5.3.46 → 5.3.47(只改第三位、不进位)。 ⑩hub=v5.3.47 - 3573079 release: v5.3.46 — ★★三件事一次修完:发布不再重复推两个一模一样的 / 工具 UI 不再糊一长串路径 / 记忆旧格式可一键清理🧹━━ 一、发布流程:为什么每次发完会立刻再推一个一模一样的 ━━ 你禁止过这件事,我之前也没做到 —— 根因找到了,是配置文件与自己文件头的注释互相矛盾:
dev-build.yml第 2 行白纸黑字写着「2026-10-20 起:主流程只推 main,本 workflow 仅供实验分支用手动触发」,但on:里还留着push: branches: [\"adopt-3.0.0\"]。 于是每次发版的完整链条是:rc release只推 main(跑build.yml,正式发版)→ 我为了保持分支同步补推 adopt-3.0.0 → 触发dev-build.yml跑一遍完整构建 → 你在 GitHub 上看到「刚发完,又冒出一个一模一样的,唯一区别是 adopt-3.0.0」。 它其实什么都没发布(dev-build不创建 release、不碰 main,注释里也这么写),但看起来就像重复发版,而且白烧一次 CI。 修复:删掉push触发,只留workflow_dispatch。今后补推 adopt 不会再产生任何构建;需要临时验证实验分支时手动触发即可。 验证:全库 grepadopt-3.0.0,剩余的只出现在注释与 workflow 名称里,没有任何自动触发。 ━━ 二、工具 UI:不再显示路径,改为文件名 + 内容 ━━ 你说的那句「路径在我看来就是一串无意义的乱码,用户无法识别」是对的。而且代码里本来就有两套做法:workspace_read_file/workspace_write_file早在 v3.11.24 就改成「标题只写『读取文件』,完整路径放展开详情」,但workspace_edit_file和workspace_show_file一直还在标题里拼完整路径 → 你看到的正是这两个。 现在统一:标题 = 文件名(rincore.md,不是/workspace/KEEP-学习分区/.../rincore.md);正文摘要照旧显示内容(前 10 行 + 语法高亮);完整路径只在你自己点开的详情面板里,以次要小字出现在文件名下面(那时你才需要精确定位)。 编辑详情面板的标题、展示文件的详情标题、多文件时的「首个 + N」全部同一口径。 ━━ 三、记忆系统:三个结构问题 ━━ 你实测报告里的三条,逐条处理: ①/profile.md存在,但说明里写「There is NO profile file」—— 口径矛盾 这是我的表述不严谨:那句是给模型新建记忆时的指令,却读起来像「库里不存在这个文件」。改为 「Do not create a profile file」(禁止新建),并补一句:如果存在旧的/profile.md,把它当普通 fact 条目(不是特殊层、不再置顶),你顺手编辑它时,把还有用的行搬进/projects/或/topics/,不要继续往里长。 这样「存量数据」与「规范」不再打架。 ②/areas/、/people/不在规定路径内 —— 规范与存量不一致 这两个目录是原版 3.1.0 的布局,你的存量文件还在那里;我在 v5.3.44 改成/playbooks/、/projects/、/topics/时没说明存量怎么办。现在明确:旧布局继续可读 ——/areas/x.md按kind: project计权、/people/x.md按kind: fact计权;不新建(新知识写进三个新目录),编辑旧文件时顺手把有用内容搬过去。 ③ 13 个旧格式根文件重复 —— 缺一个能清理的入口 底层(purgeLegacyFiles)从 v5.3.23 就存在,判据是「content 不以---开头」—— 新架构写入必带 frontmatter,所以这条判据不会误删任何新记忆。但一直没有 UI 入口。 现在:记忆页顶栏多一个删除图标,只在库里真有旧格式条目时出现;点它弹确认,写明「会删多少条、它们当前不注入模型、只删这些旧条目、不可撤销」→ 确认后一次清完。 顺带修正我自己的一处类型错误:vm.memories的每个元素是MemoryFile(不是AssistantMemory),旧格式判据要用!isStructured而不是isLegacy—— 编译前自查发现并改掉。 ━━ 四、诚实边界 ━━ ①旧格式清理是物理删除且不可撤销(这是你要求的「不要再看到重复」的必然代价),所以放在二次确认后面,且只匹配「没有 frontmatter」的条目;②**/profile.md与/areas/、/people/不做自动迁移** —— 自动搬移会改变归属与 kind,风险高于收益;现在模型知道该往哪写、旧文件按什么计权,你点头我再做迁移;③工具 UI 改动只影响显示,工具行为、参数、返回值一个字没动;④无安装实测(沙箱无设备),以 tree-sitter 三文件 PASS +rc check双 PASS 为据。 ★版本纪律:5.3.45 → 5.3.46(只改第三位、不进位)。 ⑩hub=v5.3.46 - d886d5c release: v5.3.45 — ★★记忆系统工业级验收:用真实源码实跑 18 项,修掉 4 个「会让人以为记忆又坏了」的真问题🧠这一版不是再打补丁 —— 是把记忆全链路的读写路径逐条对账,并用 kotlinc 编译真实源码实跑验证。 ━━ 一、方法:不再靠「读一遍觉得没问题」━━ 把记忆拆成 表 → DAO → 仓库门面 → 工具 → 注入 → UI → 做梦 → 迁移 八段,对每一条读写路径检查它是否遵守同一套不变式(唯一表 / 归属互斥 / 软删过滤 / path 多形态 / 跨范围拒绝 / 挂载三档 / 来源二维 / 越权防线 / kind 权重 / Auto 注入)。 然后把真实
MemoryRepository.kt+MemoryFileDAO.kt+MemoryFile.kt用 kotlinc 编译(补 Room 注解、Log、Assistant 的最小 stub),配一个内存版 DAO 实跑 18 项断言。 ━━ 二、实跑抓到 4 个真问题(都属「不报错但用户会以为记忆又坏了」)━━ ① 删掉的记忆,模型重新写入 → 内容进去了却永久不可见 旧实现无条件保留invalidatedAt,且返回成功。用户删掉「饮食偏好」后模型再记同一主题:内容写进库了、writeFile说 Success、记忆页却看不到 —— 两边都以为成功,实际什么都没发生(你之前报的「模型说记住了、我却找不到」的另一层原因,此前只归因到 UI 死代码)。 语义仍以 v5.3.12 为准:用户明确删掉的,不自动复活(那条既有测试锁的是用户意图,我一开始改成"复活"被它拦下了 —— 这是硬门禁该起的作用)。 真正要修的是静默:现在命中已删除路径 → 明确拒绝并说明「这条已被你删除,不会自动恢复;请换一个路径,或让用户确认恢复」。模型能据此换路径,你也终于能看见它为什么没记进去。 ②getFile是公开 API,却能读到已删除的条目 它内部不过滤软删(这是给「写入前判存」用的)。实跑时「删除后不可读」这条断言就是失败的 —— 证明公开面语义不清。现在拆成:getFile= 读可见记忆;新增私有getFileAny专供判存。 ③ 模型侧删除老记忆删不掉 模型走deleteMemoryChecked→ 直调 DAO 的单形态invalidate,对 v5.3.7 迁移来的无前导斜杠老 path 恒 0 行命中 → 模型说「已删除」但条目还在。改走invalidateFile(六形态),失败时明确报错不再假装成功。 ④copyFiles会覆盖目标助手的同名记忆 复制助手时直接 upsert。copyMemories在 v5.3.19 已改成「冲突就跳过并记日志」,copyFiles漏了 —— 现在补上(虽然当前零调用点,但它是公开 API)。 ━━ 三、18 项实跑断言(全部通过)━━ 写入可读 / 删除不可读 / 删除后列表空 / 重写必须复活 / 复活后内容是新写的 / 复活后回列表 / 对已删条目 append 必拒 / replace 必拒 / 老 path 可见 / 带斜杠删除老记忆必命中 / 删除后不可见 / 模型侧删除老 path 必生效 / 跨范围覆盖必拒 / 挂对话后 folderId 必清空 / 挂对话后 conversationId 正确 / MOUNT_FOLDER 全见 / MOUNT_ASSISTANT_ONLY 只见助手级 / MOUNT_CURRENT_CONVERSATION 见助手级+本对话。 另加 3 条回归测试进白名单硬门禁(复活 / 拒绝局部编辑 / 老 path 删除),锁死这三条语义。 ━━ 四、关于「工业级」的诚实判据 ━━ 我能给出的保证是:存储层只剩一张表、一条写入门面、一套删除语义、一套注入判据,且这三个「唯一」都被实跑验证过。 我不能给出的是:你库里的内容质量 —— 那 40+ 条版本流水账不会因为代码变好而变得有用,它们仍然按设计不注入。要让模型真正「懂你在做什么」,需要按工作手册的形态重新写几条(发版流程、环境重建、常见坑),几条就够,不需要几十条。 ━━ 五、MIKA 双软件共存的想法(你问的)━━ 我认为技术上可行,而且在架构上我们正好有个天然优势:我们已经是「一个应用 + 多个能力域」的形态(工具区、工作区、Provider 都是插件式),再加一个「人格/子应用」维度不是推倒重来。 但要澄清一个关键点:Mika 和 RinCore 争的不是「谁做助理」,而是「上下文归谁」 —— Mika 的强项是「知道你是谁」(称呼、日程、口味),RinCore 的强项是「知道活怎么干」(命令、路径、坑)。你自己说过不要用户画像,那句话对 RinCore 成立、对 Mika 恰恰相反。 所以我建议的形态是:共享沙箱与文件系统(同一套 proot + 工作区),记忆按「域」分区而不混 —— Mika 域记人,RinCore 域记事,跨域只通过「显式引用」传递(类似 skill 的引用方式),而不是自动合并。这样两个都保持各自最擅长的部分,又不会互相污染。 这一版先留了扩展点(MemoryRepository的 memoryId 本来就是「一个字符串桶」:助手 ID / GLOBAL / 将来可以是mika:<userId>),没有实现多状态,按你说的等 Mika 稳定。 ━━ 六、诚实边界 ━━ ①实跑用的是内存 Fake DAO,验证的是 Repository 的语义,不是 Room/SQLite 的真实行为(SQL 层由 CI 的编译 + 已有测试保证);②无安装实测;③deleteMemory(id)(Operit 脚本用)仍硬编码 GLOBAL 库 —— 现状与 Operit 的addMemory一致,不是新问题,已记录;④getFilesOfConversationFlow无 assistantId 过滤(conversationId 是全局 Uuid,实际不串),已记录待后续收敛。 ★版本纪律:5.3.44 → 5.3.45(只改第三位、不进位)。 ⑩hub=v5.3.45 - 4812228 release: v5.3.45 — ★★记忆系统工业级验收:用真实源码实跑 18 项,修掉 4 个「会让人以为记忆又坏了」的真问题🧠这一版不是再打补丁 —— 是把记忆全链路的读写路径逐条对账,并用 kotlinc 编译真实源码实跑验证。 ━━ 一、方法:不再靠「读一遍觉得没问题」━━ 把记忆拆成 表 → DAO → 仓库门面 → 工具 → 注入 → UI → 做梦 → 迁移 八段,对每一条读写路径检查它是否遵守同一套不变式(唯一表 / 归属互斥 / 软删过滤 / path 多形态 / 跨范围拒绝 / 挂载三档 / 来源二维 / 越权防线 / kind 权重 / Auto 注入)。 然后把真实
MemoryRepository.kt+MemoryFileDAO.kt+MemoryFile.kt用 kotlinc 编译(补 Room 注解、Log、Assistant 的最小 stub),配一个内存版 DAO 实跑 18 项断言。 ━━ 二、实跑抓到 4 个真问题(都属「不报错但用户会以为记忆又坏了」)━━ ① 删掉的记忆,模型重新写入 → 内容进去了却永久不可见 旧实现无条件保留invalidatedAt。用户删掉「饮食偏好」后模型再记同一主题,库里有内容、记忆页看不到 —— 这正是你之前报的「模型说记住了、我却找不到」的另一层原因(此前只归因到 UI 死代码)。 现在按动作区分:整体写入(memory_write)= 复活(写进来就该可见);局部编辑(str_replace / append)= 拒绝并明确告知「这条已删除,要恢复请用 memory_write」。 ②getFile是公开 API,却能读到已删除的条目 它内部不过滤软删(这是给「写入前判存」用的)。实跑时「删除后不可读」这条断言就是失败的 —— 证明公开面语义不清。现在拆成:getFile= 读可见记忆;新增私有getFileAny专供判存。 ③ 模型侧删除老记忆删不掉 模型走deleteMemoryChecked→ 直调 DAO 的单形态invalidate,对 v5.3.7 迁移来的无前导斜杠老 path 恒 0 行命中 → 模型说「已删除」但条目还在。改走invalidateFile(六形态),失败时明确报错不再假装成功。 ④copyFiles会覆盖目标助手的同名记忆 复制助手时直接 upsert。copyMemories在 v5.3.19 已改成「冲突就跳过并记日志」,copyFiles漏了 —— 现在补上(虽然当前零调用点,但它是公开 API)。 ━━ 三、18 项实跑断言(全部通过)━━ 写入可读 / 删除不可读 / 删除后列表空 / 重写必须复活 / 复活后内容是新写的 / 复活后回列表 / 对已删条目 append 必拒 / replace 必拒 / 老 path 可见 / 带斜杠删除老记忆必命中 / 删除后不可见 / 模型侧删除老 path 必生效 / 跨范围覆盖必拒 / 挂对话后 folderId 必清空 / 挂对话后 conversationId 正确 / MOUNT_FOLDER 全见 / MOUNT_ASSISTANT_ONLY 只见助手级 / MOUNT_CURRENT_CONVERSATION 见助手级+本对话。 另加 3 条回归测试进白名单硬门禁(复活 / 拒绝局部编辑 / 老 path 删除),锁死这三条语义。 ━━ 四、关于「工业级」的诚实判据 ━━ 我能给出的保证是:存储层只剩一张表、一条写入门面、一套删除语义、一套注入判据,且这三个「唯一」都被实跑验证过。 我不能给出的是:你库里的内容质量 —— 那 40+ 条版本流水账不会因为代码变好而变得有用,它们仍然按设计不注入。要让模型真正「懂你在做什么」,需要按工作手册的形态重新写几条(发版流程、环境重建、常见坑),几条就够,不需要几十条。 ━━ 五、MIKA 双软件共存的想法(你问的)━━ 我认为技术上可行,而且在架构上我们正好有个天然优势:我们已经是「一个应用 + 多个能力域」的形态(工具区、工作区、Provider 都是插件式),再加一个「人格/子应用」维度不是推倒重来。 但要澄清一个关键点:Mika 和 RinCore 争的不是「谁做助理」,而是「上下文归谁」 —— Mika 的强项是「知道你是谁」(称呼、日程、口味),RinCore 的强项是「知道活怎么干」(命令、路径、坑)。你自己说过不要用户画像,那句话对 RinCore 成立、对 Mika 恰恰相反。 所以我建议的形态是:共享沙箱与文件系统(同一套 proot + 工作区),记忆按「域」分区而不混 —— Mika 域记人,RinCore 域记事,跨域只通过「显式引用」传递(类似 skill 的引用方式),而不是自动合并。这样两个都保持各自最擅长的部分,又不会互相污染。 这一版先留了扩展点(MemoryRepository的 memoryId 本来就是「一个字符串桶」:助手 ID / GLOBAL / 将来可以是mika:<userId>),没有实现多状态,按你说的等 Mika 稳定。 ━━ 六、诚实边界 ━━ ①实跑用的是内存 Fake DAO,验证的是 Repository 的语义,不是 Room/SQLite 的真实行为(SQL 层由 CI 的编译 + 已有测试保证);②无安装实测;③deleteMemory(id)(Operit 脚本用)仍硬编码 GLOBAL 库 —— 现状与 Operit 的addMemory一致,不是新问题,已记录;④getFilesOfConversationFlow无 assistantId 过滤(conversationId 是全局 Uuid,实际不串),已记录待后续收敛。 ★版本纪律:5.3.44 → 5.3.45(只改第三位、不进位)。 ⑩hub=v5.3.45
构建:gate(编译+测试)→ build(assembleRelease)→ 正式版 Release