Skip to content

runtime /packages domain 持久化不对称:enable/disable/uninstall 直写 in-memory registry,install/update 却走 protocol service 落 sys_packages #5047

Description

@xuyushun441-sys

未认领,待核实定性。 #4311 runtime 片(PR #5045)实施中的观察,修复需改运行时行为、超出该片边界,按 PD#10 立案。

观察(来自 packages/runtime/src/domains/packages.ts 的实读)

  • install / updatePackageManifest:优先走 protocol service 持久化到 sys_packages,服务缺席时才回落 in-memory;
  • enable / disable / get / uninstall:直接读写 in-memory registry,不经持久化路径。

若属实,后果是:enable/disable/uninstall 的状态变更重启即失 —— 管理员禁用一个包,进程重启后它复活。这与 install 路径的语义不一致,像是持久化通道接了一半。

待核实

  1. enable/disable 状态是否确实只活在内存(有没有别的落库路径,例如 boot 时从 sys_packages 重放);
  2. 若 boot 重放存在,那么不对称只是"写路径分裂"而非数据丢失 —— 定性从 bug 降为一致性债;
  3. sys_packages 的行里有没有 enabled 字段可承接。

核实第 1 条成立即标 bug 入队(维护者当前规则:只派 bug)。

关联

Activity

  1. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    核实完成 —— 原前提不成立,但挖出一个更窄的真 bug;本单改定性并收窄派发

    只读核实(全部证据带 file:line,以 origin/main 为准)推翻了正文的"enable/disable 只写内存":

    持久化存在,且是刻意设计:packages/runtime/src/package-state-store.ts:70-77 把 disabled 集合写进 <OS_HOME>/package-state/<environmentId>.json,文件头 rationale 明写"intentionally a flat file rather than a sys_* object: package lifecycle state is runtime/operational state, not project metadata"。boot 回灌路径也存在且时序自洽:app-plugin.ts:226-248 在 init(Phase 1)seed disabled 集合 → service-package/index.ts:200-253 在 start(Phase 2)从 sys_packages 重放时,registry.installPackage 消费它(registry.ts:1602-1608)把包装成 disabled。原文那种"直写内存、重启即失"的定性不成立,不按原文修。

    真 bug(本单收窄为它):empty-env 早退跳过 disabled seed

    app-plugin.ts:196-201 —— bundle 无 app 载荷时 init 提前 return,而 disabled seed 块(:226-248)在早退之后。empty env 恰恰是"包只来自 sys_packages / HTTP 安装"的场景(hydration 自己的注释就是这么写的)。后果:纯 DB 驱动的环境里,管理员禁用的包每次重启都复活。

    修法低风险:把 seed 块提到早退之前 —— 同一函数里 installDefaultHookBodyRunner / wireAuthoredTranslationSync 已因同样理由被提前(:177-195 有注释先例)。测试盲区是它没被发现的原因:全仓零测试覆盖 package-state-store / setInitialDisabledPackageIds,修复必须补上(含 empty-env 场景的回归测试)。

    记录为债、本单不修

    • 状态跨两种介质(disabled 在 OS_HOME 文件,包在 DB),容器/多副本/临时文件系统下会漂移 —— 那是 package-state-store.ts rationale 的 ADR 级取舍,要改先改 ADR;
    • DELETE /packages/:id 双写清理 sys_packages(protocol.ts:8429-8440,"so the package cannot resurrect at boot")但不清理 disabled 条目 → 同 id 重装会静默以 disabled 回来;
    • sys_packages 无 lifecycle 列(raw-SQL 表,无 spec 定义),统一到 DB 需 schema 变更。

    认领(PM 分派,收窄后)

    • session:session_018iARDqtrhQgz6fVHDeDkbQ
    • 分支:claude/issue-5047-empty-env-disabled-seed
    • 车道:cli(落点 packages/runtime/src/app-plugin.ts + 新测试)

    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    审核通过 — PR #5117 已取消草稿并挂上 auto-merge(SQUASH)

    修复是最小行序调整,验证是全真件的。 seed 提到 empty-env 早退之前,抽成带文档注释的私有方法,注释对齐同函数两个先例。要害在测试的成色:

    • 回归测试跑在真 LiteKernel + 真 ObjectQLPlugin + 真状态文件上,不是 mock 拼装;hydration 断言更进一步 —— 把 empty-env boot 出来的真 registry 交给真的 PackageServicePlugin.start() 重放 sys_packages,为此按 service-job 先例加了 devDependency + vitest alias,宁可引真件也不重实现被测对象 —— 重实现的 hydration 验证不了任何事。
    • 反向验证钉在正确的钉眼上:行序改回后恰好两条 empty-env 用例红(enabled: true / status: installed 对 false / disabled),非 empty 用例保持绿,store 单测独立于行序 —— 红的形状证明测试测的就是这个 bug,不是别的。
    • 盲区补齐:package-state-store 从零覆盖到 12 例,含环境 id 路径消毒(../../etc/evil 逃不出目录)与坏 JSON 降级为空集 —— 核实报告点名的"为什么没被发现"被正面回答。
    • 新增的 getService('objectql') 触点在无引擎 kernel 下仍能 boot(case 7 钉住)—— 行序调整没有引入新的启动依赖。

    runtime 全量 1150 测试绿(含 #5045 刚接的 typecheck),六道 check 门禁绿。lockfile 仅 devDependency 3 行,OSV 修复已在 main,无红。

    三条一致性债照核实结论留档不扩面。dev 另纠了资源纪律文档里的一处错误:--concurrency 应为 --workspace-concurrency 且置于 --filter 前 —— 这个坑本会话已有四个 agent 踩过,值得进 skill 修订素材。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions