Repository navigation
[Decision] 一个应用能否声明依赖另一个应用并引用其对象(ADR-0019 推迟的应用间依赖) #25403
Description
Ruled: 6369704387 · batch #7 item 1 · letter B′ · 2026-10-23T16:28:30Z (B narrowed by the maintainer: keep the manifest dependencies declaration and the save door's closure as the reference mechanism; add only the pre-install check — dependency installed and version in range, else refuse with a clear message, filling the reserved dependency_missing category; defer uninstall protection, upgrade interception, topological / cascade install and any owner-side publish marker to named later items in the ADR; permissions unchanged; first consumer = a HotCRM module shipping alone)
立卡门类别:② 只有维护者能做的决定。动手的读者:裁定后由 domain:spec 席起草 ADR,并按 ADR 切实现卡。
查重(含 closed,仓内语义检索):
inter-App dependency→ 0 张;cross-app reference→ 0 张;per-tenant enablement module→ 0 张;ADR-0019 deferred app consumer unit→ 2 张(ADR-0087 window: a^18artifact built during the v18 prerelease gets no D2 replay of an entry retired after it was built, so it fails to boot (door 1, split from #22994) #23001、ADR-0117 D1 声明面:ownership 枚举扩展 'business_unit'(须与列注入同 PR 或严格后置) #5678,均无关);ADR-0028 sandboxed apps namespace isolation→ 2 张(runtime(sandbox): a sandboxed body whose rejection cannot be flattened (throw '', a null-prototype object, a message getter that throws) is never recorded, so the action door waits for the 30 s wall-clock ceiling and answers 500 #23723、[Decision] security(runtime): may an app-authored body still READ the stored-metadata family's tables, served projected, or is that refused too (#21454 option C) #21594,均无关);app dependencies manifest another app→ 3 张(packages: manifestdependenciescan be set only at create —PATCH /api/v1/packages/:idpersists only name / description / version, so a save refused with "declare a dependency" has no remedy short of recreating the package #24670、metadata-protocol installPackage parses only the manifest id, so the primitive stores manifests the public install door refuses (type 'application', scope 'environment' from cloud's AI authoring) #24086、[finding] AppPlugin's security-metadata registrar drops a manifestpermissionsit cannot read, at both arms, silently — the second consumer of the #18031 two-readings collision #18034,均不涉及应用之间的依赖)。
维护者速读
今天,一个可安装的应用还不能正式依赖另一个应用。以 PSA(项目交付)为例,它要引用 CRM 的客户、商机和合同,现在只有两种做法:
- 把 PSA 做进 CRM,随 CRM 发给所有客户;
- 另出一个「CRM + PSA」组合应用,让客户换装。
同样,CRM 和 SRM 之间也无法共用客户和供应商资料。
要不要让平台支持「应用 B 声明依赖应用 A,可以引用 A 的对象,由平台管安装顺序、卸载保护和版本兼容」?
- A 维持现状:应用之间隔离,要组合的产品打进同一个发布包。
- B 开放应用间依赖(推荐):先写 ADR 定契约,等第一个真实产品需要独立安装时再实现。
- C 平台自带共享主数据:客户、供应商等对象由平台提供,各应用只引用这些对象。
选 A、B 还是 C? 已裁 B′(批 #7 第 1 项,评论 6369704387):声明 + 安装前检查;卸载保护、升级拦截、多包拓扑 / 连带安装、被引用方的发布标记写为 ADR 的推迟项;权限不变。维护者原话(2026-10-23):「包依赖本身没问题,也就是说如果 psa 依赖 crm, 安装psa 之前先检测crm有没有,没有就禁止安装,这个有必要嘛」「往往需要引用的数据并不多,可能就几个对象」「如果这样,hotcrm 岂不是真实的可以分拆出很多模块」「而每个包可以独立安装」「同意」。
os-decision-facets
- ① 项目长远合理性:B 把散落的特例收成一条契约——「声明了依赖才可跨应用引用」。现在的特例包括:发布包内可以引用(ADR-0130),保存门按依赖闭包判定,ADR-0028 D4 提议全面隔离,应用仓只能合包。A 的特例会随产品组合数增长。
- ② 实际业务拉动:今天没有已上线的产品被卡住。在途的拉动都在设计阶段:hotcrm 的 PSA 设计想移出 CRM、做成单独产品;集团系统规划里 CRM 和 SRM 要共用主数据;客户仓库要在标准产品上迭代。
- ③ 防 AI 犯错:B 以声明的依赖为闭集,未声明的跨应用引用响亮拒绝,保存门今天已经这样做(
list-view-object-outside-closure返回 422)。A 同样响亮拒绝,但会把需求推向复制对象、靠同步对齐,造成静默的数据分叉。 - ④ 创业阶段不扩散:B 新增四项永久义务:安装顺序、卸载保护、跨应用版本兼容、跨应用权限语义。A 不新增义务。所以按 ④ 只调时序:先定契约,后实现。
Prior rulings read: inter-app,cross-app,app dependency,shared data model → 2 hits; none; thread: not read (no --card); repo: objectstack-ai/objectstack
(两处命中都是 ADR-0028 的 D4、D6,状态为 Deferred / proposed,不是已生效的裁决。)
一句话问题
客户先装了 CRM,以后想加 PSA。做不到「再装一个 PSA」:要么 PSA 早就随 CRM 装给了所有客户,要么客户换装一个更大的组合应用。
背景
- ADR-0019 把「共享 / 发布的数据模型 + 应用间依赖」列入 Out of scope (explored, deferred — a separate gated bet),要求另立 ADR 并做明确的 go/no-go。
- ADR-0130 为同一发布包内的多个包开了路:一个 artifact、N 个 package,按依赖拓扑注册。hotcrm 在这个前提下实测了跨包规则(
docs/architecture/module-split-plan.md):- R1,查找或主从关系指向另一个包的对象:接受;
- R3,包自己的应用导航指向另一个包的对象:拒绝;
- R4,钩子挂在另一个包的对象上:拒绝。
- 运行时保存门已经有依赖闭包:写入某个包的元数据,只能引用以下对象,否则拒绝并提示「declare a dependency」(fix(lint)!: the save door refuses a list view written into a package and bound outside its closure #24630、packages: manifest
dependenciescan be set only at create —PATCH /api/v1/packages/:idpersists only name / description / version, so a save refused with "declare a dependency" has no remedy short of recreating the package #24670,feat(runtime): PATCH /api/v1/packages/:id edits a package's manifest dependencies #24703 已允许事后修改dependencies):- 本包的对象;
- manifest 里
dependencies声明的包的对象; - 平台对象;
- 不属于任何包的租户对象。
- 缺的是可安装应用之间的正式契约:安装时检查被依赖的应用、按依赖排序、卸载保护、版本范围,以及跨应用引用的权限语义。
Governing text
docs/adr/0019-app-as-consumer-unit.md§Out of scope:「Shared/published data model + inter-App dependencies (cross-app data sharing without a "suite").」docs/adr/0028-metadata-naming-and-namespace-isolation.mdD4(状态 Deferred):「Atype: apppackage may reference its own metadata … but may not reference metadata owned by another app.」docs/adr/0130-release-artifact-as-co-ownership-boundary.mdD2、D5:发布包内多包,按依赖拓扑注册。packages/lint/src/rule-explanations.ts的list-view-object-outside-closure:保存门按闭包判定;os validate、os build、os lint不判跨包引用,原文:「a stack there is one app, and an unresolved binding may be a dependency package's object the stack does not carry」。
协议声明、是否改协议
会改协议。选 B 或 C 需要新 ADR;ADR-0028 D4 的提议要改写;ADR-0019 原文不改,只加交叉引用,沿用 ADR-0130 的记录惯例。ManifestSchema.dependencies 这一行已经存在,B 的工作是赋予它在应用之间的安装语义。
前提(每条带 re-check 命令)
- ADR-0019 推迟了应用间依赖:
git grep -n "inter-App dependencies" origin/main -- docs/adr/0019-app-as-consumer-unit.md - ADR-0028 D4 只是提议、状态为推迟:
git grep -n "Apps are sandboxed\|^\*\*Status\*\*" origin/main -- docs/adr/0028-metadata-naming-and-namespace-isolation.md - 保存门的闭包包含声明的依赖:
git grep -n "the objects of the packages its" origin/main -- packages/lint/src/rule-explanations.ts - 构建和校验不判跨包引用:
git grep -n "judge neither" origin/main -- packages/lint/src/rule-explanations.ts - 发布包内 R3 被拒:在 hotcrm 仓运行
grep -n "^| \*\*R3\*\*" docs/architecture/module-split-plan.md 未验证:两个分别安装的 App 今天在安装门上的实际行为——是否按已由总监席核实(2026-10-23,maindependencies排序、缺依赖时是否拒绝、卸载被依赖方时是否阻止。插件层有依赖解析器,但它是否作用于应用安装未测:git grep -n "Missing dependency" origin/main -- packages/core/src/dependency-resolver.tsea33919f):安装路径不读manifest.dependencies;dependency_missing只是packages/spec/src/system/app-install.zod.ts的枚举值,全仓无生产者;packages/runtime/src/domains/packages.ts编辑dependencies只校验形状(⛔ No in-use check);packages/core/src/dependency-resolver.ts只服务 kernel 插件。即:今天不排序、不拒绝、不阻止——这正是 B′ 的安装前检查要补的。
具体问题
应用之间的引用,平台的终态是什么:
- 隔离;
- 声明依赖后可以引用;
- 由平台提供共享主数据。
选项
| 选项 | 做什么 | 客户能感知的后果 | 工作量 |
|---|---|---|---|
| A 维持隔离 | 不改。ADR-0028 D4 的方向照旧;要组合的产品打进同一个发布包 | 后加一个产品,要么它早已随主应用装给所有人,要么换装组合应用;跨应用的主数据靠导入或同步 | 无 |
| B 应用间依赖 | 写 ADR;安装门检查被依赖的应用已安装、版本在范围内,并按依赖排序;卸载被依赖的应用时阻止或提示;跨应用引用沿用保存门的闭包判定,覆盖到安装和构建;控制台显示依赖关系;定义跨应用引用的权限语义 | 先装 CRM,以后再装 PSA;卸 CRM 前提示 PSA 依赖它;未声明的引用保存时直接报错 | spec、运行时、控制台多车道,1 篇 ADR 加若干 PR;无先例(最近的同类工程是 ADR-0130 的包内多包注册,其 PR 行数未测) |
| B′ 声明 + 安装前检查(已裁) | 声明沿用 ManifestSchema.dependencies,引用沿用保存门闭包;安装前检查依赖已装且版本在范围内,否则拒绝并提示;卸载保护、升级拦截、拓扑 / 连带安装、被引用方发布标记写为 ADR 推迟项;权限不变 |
先装 CRM,以后再装 PSA;装反了被拦并提示先装 CRM | spec 席 ADR(S)+ 运行时安装前检查(XS–S)+ 文档 |
| C 平台共享主数据 | 平台提供客户、联系人、供应商、产品、合同等共享业务对象,各应用只引用这些对象,应用之间不互相引用 | 各应用天然共用主数据;但已有应用的对象要迁移到共享对象 | 平台要拥有一套业务数据模型,需要 ADR、多车道,以及现有应用的迁移;无先例 |
业务含义直译
- A:像套装软件——要么买整套,要么各用各的,中间靠导数据。
- B:像 Salesforce 的扩展包依赖基础包——主应用先装,扩展应用后装,主应用不能先被卸掉。
- C:像 Dynamics 的公共数据模型——平台自带客户、联系人等标准表,所有应用共用。
四轴论证(业务立场)
- ①:两年后,市场上会有第一方的 CRM、SRM、PSA,也会有第三方应用;客户按需先后安装是常态。只有 B 让「后加一个产品」不需要换装,也不需要把它塞给所有人。
- ②:拉动真实但还在设计阶段,所以只影响时序,不影响方向。
- ③:B 的闭集判定在保存门已经实现,出错时作者当场看到 422;A 下作者会复制对象,错误表现为两边数据静默不一致,没人当场看到。
- ④:B 的永久义务明确且有限(四项),可以写进一篇 ADR 一次定清;C 的义务是整套业务数据模型,范围大得多。
推荐与置信缺口
推荐 B。 终态是:应用之间通过显式声明的依赖引用对方对象,平台管安装顺序、卸载保护和版本兼容。这是主流平台的共同形状,参照有:
- Salesforce:扩展包依赖基础包;
- Dataverse:解决方案依赖;
- ServiceNow:跨作用域访问策略;
- Odoo:模块的
depends。
不取另外两项的理由:
- 不取 A:组合数随产品数增长,客户后加产品只能换装;
- 不取 C:平台要拥有业务数据模型,内核契约会大幅扩张。有了 B 之后,「主数据应用」可以作为一个普通应用来实现。
置信缺口:
前提 6 未实测,即今天两个分别安装的 App 在安装门上的行为;(已核实,见前提 6)- 跨应用引用的记录级权限语义未设计——依赖方的查找关系,如何受被依赖方的共享规则约束;
- ADR-0028 D4 想要的市场应用之间的隔离,在 B 下靠「必须显式声明依赖」来保留。这是否足够,需要安全方向复核。
自检:只看①选 B;②③④ 是否翻转:否。②、④ 只影响时序:先写 ADR,等第一个真实产品需要独立安装时再实现。
裁后执行
- 裁 B′(已裁,批 Remove explicit pnpm version from workflows to fix version conflict #7 第 1 项):
domain:spec席起草 ADR:声明(现有dependencies)、安装前检查(已装 + 版本在范围内,否则拒绝并提示;填AppCompatibilityCheck已预留的dependency_missing)、跨应用引用沿用保存门闭包、权限语义开放节;卸载保护、升级拦截、拓扑 / 连带安装、被引用方发布标记写为推迟项;manifest 与市场两种dependencies形状对齐;- 同一份 ADR 把 ADR-0028 D4 改为「未声明依赖不得跨应用引用」,并在 ADR-0019 加交叉引用(不改原文);
- ADR 合并后切实现卡:第一张是运行时的安装前检查(XS–S,含控制台拒绝文案);第一个消费者场景写为 HotCRM 模块单独发布(hotcrm
module-split-plan.md2026-09-14 形状,revenue 先); - 检查落地前,需要组合的产品仍按 ADR-0130 打进同一个发布包。
裁 A:在 ADR-0019 记一条 no-go 的交叉引用;hotcrm 的 PSA 按「同一发布包」写定。裁 C:先立「平台共享业务对象」ADR 的范围卡,评估现有 CRM、SRM 对象的迁移。
相关单与 PR
- ADR-0019、ADR-0028、ADR-0130
- fix(lint)!: the save door refuses a list view written into a package and bound outside its closure #24630(
list-view-object-outside-closure)、packages: manifestdependenciescan be set only at create —PATCH /api/v1/packages/:idpersists only name / description / version, so a save refused with "declare a dependency" has no remedy short of recreating the package #24670 / feat(runtime): PATCH /api/v1/packages/:id edits a package's manifest dependencies #24703(dependencies可事后修改) - hotcrm:
docs/architecture/psa-module-plan.md、docs/architecture/module-split-plan.md(R1–R4)、docs(requirements): REQ-0007 order-to-cash module — what sales must see after the contract hotcrm#2251(订单与回款模块,列出了「按租户启用模块 / 应用间依赖」这一上游需求)
Activity
os-elon-musk commentedon Oct 23, 2026
Ruling: batch #7 item 1 · letter B′ · maintainer 「同意」 2026-10-23T16:28:30Z
What B′ is (the card's B, narrowed by the maintainer's own words in the director thread; the letters A / B / C as filed and the D「发布对象」 variant the director offered at 16:10 were not chosen):
ManifestSchema.dependencies({ packageId: versionRange }, manifest.zod.ts); the save door's closure already honours the declared packages' objects (list-view-object-outside-closure, fix(lint)!: the save door refuses a list view written into a package and bound outside its closure #24630 / packages: manifestdependenciescan be set only at create —PATCH /api/v1/packages/:idpersists only name / description / version, so a save refused with "declare a dependency" has no remedy short of recreating the package #24670 / feat(runtime): PATCH /api/v1/packages/:id edits a package's manifest dependencies #24703), so cross-app references need no new reference mechanism and no owner-side「published object」flag.dependency_missingcategory thatAppCompatibilityCheckSchema(packages/spec/src/system/app-install.zod.ts) already reserves and that today has no producer.The maintainer's reasoning, verbatim, for the ADR author (director thread, 2026-10-23):
Workload of the chosen letter: one ADR by the
domain:specseat (S, half a day to a day): the contract above; ADR-0028 D4 rewritten to「no cross-app reference without a declared dependency」; a cross-reference in ADR-0019 (text unchanged, ADR-0130's recording convention); the deferred gates listed by name; the open permission section; the manifestdependenciesrecord and the marketplacePackageDependencySchemaarray (package-version.zod.ts) aligned. Then one runtime card for the pre-install check (XS–S) with the console's refusal text, plus docs. The first consumer scenario the ADR should name is a HotCRM module shipping on its own (hotcrmdocs/architecture/module-split-plan.md, the 2026-09-14 shape: sales app + service / revenue / marketing;revenuefirst per ADR-0130 §1.3(c)); until the check lands, composite products keep shipping as one artifact under ADR-0130. The card's premise 6 is answered: nothing at install readsmanifest.dependenciestoday (dependency_missingis an enum only;packages.tsvalidates the key's shape;core/dependency-resolver.tsserves kernel plugins), which is exactly the gap item 2 closes.Labels:
needs-user-decision→pm:queue; thedomain:specseat claims and drafts (裁不派). Brief with the four appendices (B business view, D variant, why the check is needed, HotCRM module split state): https://claude.ai/artifact/DKvzE9D3NBheC17fJEfbRW (private to the maintainer's account).Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
From
repo:hotcrmseat 1 ·os-elon-musk· sessionsession_014xanhPEmGTMTfVH4UokwJT· 2026-10-23T16:46ZTwo inputs for the ADR this card's ruling (B′,
6369704387) asks for. They come from filing HotCRM's module-split cards: hotcrm#2270 packages revenue and marketing inside the one artifact; hotcrm#2271 ships revenue on its own once the pre-install check is released. This is information for the drafting seat. HotCRM does not rule on either point.namespace: 'crm'(src/sales/index.ts:76,src/service/index.ts:151). ADR-0130 D1 makes a shared namespace legal only inside one release artifact.NamespaceConflictErrorfor a non-co-owner package whose namespace is already installed (packages/objectql/src/registry.ts:5942). ADR-0048 D2 says the gate still refuses to co-install the two.needs-user-decision.not_planned(2026-09-12).5645322259) says a module carved out as its own artifact ships its own permission sets, and that the card is not reopened for that. Its survey (5623464256) measured that module-owned sets already ship.navigationContributions)—— ADR-0130 分拆第二阶段,不挡发版 #14488 as "filed, not decided". That line is stale.Generated by Claude Code