Files
qinglong/docs/adr/ADR-0185-workspace-package-boundary-consolidation.md
T

8.4 KiB
Raw Blame History

ADR-0185Workspace Package 边界审计与最小 Profile 交付梯度

  • 状态:Accepted
  • 日期:2026-07-28
  • 关联:RFC D-02、D-05、D-35、D-37、D-40、D-42、D-169、D-174、D-175 ADR-0088、ADR-0093、ADR-0179、ADR-0184

2026-08-01 更新:ADR-0257/D-240 supersede 本 ADR 中“所有 Profile 共用 16 MiB import RSS delta”的部分,并明确 npm 开发包与最终运行制品的裁剪边界。本文的 package/authority 边界和分层 files/bytes 预算保持有效;后续 22→19 package 收敛见 ADR-0217 与 ADR-0243。

背景

QingLong 3.0 当前有 22 个 workspace package。包边界在孵化阶段帮助隔离了 SQLite/PostgreSQL、常驻 runtime/短生命周期 authority、Edge/Cluster 和可选 AI 也阻止了 legacy 根包反向进入 3.0。部分 package 只有一至三个源码文件,因此需要 确认它们是有效交付边界,还是仅把源码目录误建成 package。

workspace package 不是普通源码目录:

  • 每个 package 都增加 manifest、exports、tsconfig、build/test closure 和 lockfile importer 维护成本;
  • 错误拆分会产生没有独立消费者、产物或 authority 的线性依赖链;
  • 低配设备的主要成本来自最终 Profile 产物和 native/第三方依赖闭包,不是仓库中 package 数量本身;
  • 反过来,按文件数合并可能把 Secret 写 authority、GC/destructive authority、 POSIX Owner 代码或 application 依赖带入更小的 Edge 产物。

审计事实

1. 一文件 package 不等于无效 package

22 个 package 中只有 3 个 package 只有一个生产源码文件:

  • @qinglong/local-command-file:161 LOC,无生产依赖,被 local-application、 local-owner-cli 和 local-owner-maintenance 三个 package 使用;
  • @qinglong/local-identity327 LOC,生产只依赖 runtime-core;它让通用 credential authentication 不反向依赖 POSIX Owner console、keyring 或 SQLite composition
  • @qinglong/local-secret-admin489 LOC,是短生命周期 Secret 明文写入/强认证/ Policy/audit authority,刻意不被常驻 application 依赖。

三者分别满足共享依赖反转、运行时认证边界和高权限代码排除,不能按 LOC 自动合并。

2. Profile 线性源码链实际是独立交付梯度

从 manifest 看,local-profile → local-adopted-profile → local-application 各只有一个 直接生产消费者,表面像可合并的 composition chain。但 artifact audit 实际发布三个 不同层级:

edge / standalone storage-only
  runtime-core + local-sqlite + local-profile

edge-adopted / standalone-adopted
  storage-only + local-admin + local-adopted-profile

edge-application / standalone-application
  adopted + command-file + secret + process + execution + local-application

npm dependency 是 package 级而不是 export subpath 级。若把 profile 源码改成 local-application/profile/*,只安装该 subpath 仍会解析 local-application 的全部 production dependencies,把 command-file、Secret、Process 和 Execution 闭包带入 storage-only 路由设备产物。把 adopted-profile 合入 application 也会消灭中间的 adoption/fence 独立产物。

因此这两个小 package 是制品裁剪边界,不是单纯代码分层。

决策

1. Package 是交付与 authority 边界,不是代码分层单位

新增或保留 workspace package 必须至少满足一项:

  1. Profile/依赖隔离:只进入特定部署 Profile,或隔离 native/大型第三方依赖;
  2. 进程/凭据隔离:拥有独立 binary、数据库角色、Kubernetes RBAC、OS 权限、 credential 或 destructive authority
  3. 发布边界:需要独立制品、平台矩阵或供应链审计;
  4. 依赖反转:是被两个以上上层 package 共享的稳定叶子,合并会迫使低层依赖 composition root
  5. 运行时安全边界:即使源码很少,也必须证明常驻产物不能携带该 authority。

仅有“领域名称不同”“目录更整齐”“当前只有一个文件”都不能单独成为 package 理由。领域内聚继续通过目录、文件和显式 package subpath 表达。

2. 当前 22 个 package 保持不变

  • 保留 local-profile 作为 storage-only Edge/Standalone 制品边界;
  • 保留 local-adopted-profile 作为 legacy activation fence 的增量制品边界;
  • 保留 local-command-filelocal-identitylocal-secret-admin 的共享或 authority 边界;
  • runtime-corelocal-sqlitecluster-postgres 即使文件多,也不再按领域对象 拆成 workspace package;它们通过显式 subpath 和源码目录管理内聚,分别承担 Profile-neutral contract、单机 storage adapter 和 Cluster storage adapter 的单一 发布职责。

只有在 storage-only/adopted 独立产物被明确取消,或引入可证明按 subpath 裁剪 production dependency 的新制品技术后,才能重新评估 Profile 合并。

3. 建立 package budget

  • QL3 workspace importer hard cap 继续为 22
  • 新 package 必须在 RFC/ADR 中声明满足的边界条件、目标 Profile、生产消费者、 third-party/native dependency 变化和制品预算;
  • 若不能证明新的交付/authority 边界,应优先新增现有 package 的显式 subpath
  • 超过 22 必须同一变更合并或删除至少一个既有 package,或显式修改本 ADR
  • package 数不是性能 KPI,最终仍以 Edge/Standalone/Cluster/Worker 独立产物的 bytes、files、loaded modules、RSS 和启动门为验收依据。

4. Profile 资源门按真实交付层级区分

统一 4 MiB/512-file 门已经无法同时表达 storage-only、adopted compatibility 和完整 application:当前 storage-only 仍低于该门,但 adopted 在没有加载完整 application runtime 的情况下已达到约 4.51 MB/530 files。采用以下独立上限,RSS 均保持 16 MiB:

  • storage-only4 MiB / 512 files
  • adopted compatibility5 MiB / 576 files
  • application5 MiB / 640 files
  • application + AI6 MiB / 768 files。

这不是把同一个失败门静默抬高:artifact report 必须输出 Profile 名、精确 package 集合和实际上限,storage-only 的路由设备门完全不变;adopted 仍比 application 少 64 个文件预算,且只用于需要 legacy write fence 的兼容部署。

不采用方案

将 Profile 两个小包合入 local-application

源码移动本身风险低,但会因 package-level production dependencies 扩大 storage-only 和 adopted 制品,直接违背路由设备最小依赖目标。

按 LOC 或文件数自动合并

会把 Secret admin、Owner maintenance 等高权限代码带入常驻产物,也无法识别共享 叶子造成的依赖反转。

每个领域对象一个 package

会把 runtime-core 和 storage adapter 拆成大量 importer,增加 lockfile、构建、 发布和供应链成本,但没有新增运行时隔离。

把全部本机包合成 @qinglong/local

会让 storage-only、adopted、application、Owner CLI、maintenance/destructive authority 和 Secret 写 authority 共享同一发布单元,破坏当前可执行的 dependency/ source boundary。

验收证据

  • GitNexus 对六个 Profile/adopted bootstrap 及四个内部 helper 的 upstream impact 均为 LOW,0 条执行流程;这说明源码移动容易,但不能证明制品合并合理;
  • manifest 审计确认 3 个一文件 package 的消费者和 production dependency 边界;
  • artifact assembly 确认 storage-only、adopted、application 和 AI on/off 是不同 package chain,而不是同一制品的重复 wrapper;
  • storage-only 门通过:edge/standalone 为 4,062,246/4,062,306 bytes、486 files、 40 loaded modulesadopted 独立门通过:4,510,432/4,510,516 bytes、530 files、 43 loaded modules
  • D-174 后四种 application 产物门继续通过:非 AI 4,915,3624,915,506 bytes、 615 files、90 loaded modulesAI 5,593,1835,593,339 bytes、659 files、 89 loaded modules
  • 本轮没有保留任何 Profile 源码移动、manifest、lockfile 或 importer package hard-cap 变更,最终代码拓扑仍为 22 package;只新增 adopted 专属制品预算。

后续

每增加 5 个 D-series vertical slice,或 QL3 package 数拟超过 22 时重新生成相同 审计表:源码文件/LOC、生产消费者、依赖闭包、exports/binary、authority 和实际 Profile 制品归属。合并候选必须同时证明源码内聚和制品/authority 不回归。