Skip to content

[Decision] ADR-0146 rewrite (#28237): is an undeclared same-named item of a non-pair dependent package refused at publish like the pair's, or kept as that package's own item — and is action overridable in this epic #28364

Closed

Description

@objectstack-fleet

epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848

Part of #28236 · blocks #28237 (ADR-0146 rewrite, draft PR #28295) · lane: domain:skills (docs/adr/**) · opened at the maintainer's request in a direct desktop session, verbatim: 「开一个决策卡」. ⛔ Not a claim.

一句话问题

客户装了基础包「项目管理」,又装了一个依赖它的普通包「移动外勤」(不是配对的 .custom)。「移动外勤」里存了一个和基础包同名的视图,又没有声明它要覆盖谁。这条记录发布时该被拒,还是允许它作为「移动外勤」自己的视图存在?ADR-0146 改写稿对这一点给了三个互相矛盾的答案,写发布门禁的卡(N-09)没法照着实现,所以 #28237 合不进去。第二问顺带一起裁:动作(action)在本 epic 里能不能被覆盖。

背景与 Governing text

  • epic 裁定 Q1(2026-10-30,「等我拍板的 8 个问题,全部同意」):未声明的同名项,发布时拒绝。范围没说是「所有依赖包」还是「只有配对包」。
  • 改写稿三处矛盾(PR docs(adr): rewrite ADR-0146 as the package-level override record — paired <base>.custom, one declared override shared by code and Studio; dated pointers on six Status lines #28295 head af70e268;契约复核 round 1 FAIL,记录 6538795712,Must-fix 1):
    • D2(:206–209):「.custom 的项,或任何依赖包的项」,同名且未声明的,发布时拒绝。
    • D8 第 2 条(:396–399):依赖包里同名未声明的项「是那个包自己的项,按它自己的地址读取」,只有配对包的才在发布时拒绝。
    • D9 第 1 条(:419–421):流程这边继续「遮蔽并告警」未声明的同名运行时记录,跟现在一样。
  • ADR-0048 §3.3(main :236–242):两个包合法地各出品 page/home,各自按自己的包读取。⚠️ 那里说的是没有依赖关系的两个包。本卡只问依赖闭包内的同名,即一个包用了它所依赖的包里已有的名字。
  • ADR-0126 §7.1(main :362):「⛔ No same-name clone in a second package」,流程的同名克隆本来就禁止。
  • metadata-protocol: a same-named item in a dependent package replaces the base package's item for package-less reads and in the base app #28238 的实测缺陷(showcase,d6c0a75f1a)正是非配对依赖包 com.default.acme-custom 存了基础包的同名视图 showcase_task.default,导致基础应用显示 2 列。读取一侧维护者已裁 A(6540076460):基础应用不再列出那条。
  • 第二问:改写稿 D2「Which types」(:217–220)把 action 列为可覆盖类型,没有附加条件。ADR-0126 §3 把 action 归在行为类 Regime C(停用加克隆),维护者原话「动作 可能是需要开关的,因为有的 action 我不想启用。」(ADR-0126 :20)。epic 另裁「动作开关不入 epic」。同为行为类的 flow 在 D9 有四个条件,action 一个都没有(复核 Should-fix 9)。

协议声明: 本卡只定 ADR 文本,不改 spec 键。门禁由 N-09 实现,类型清单由 N-08 收口。

前提(每条带 re-check)

  1. 三处矛盾文本仍在 PR 当前 head。re-check:git fetch origin claude/project-thread-j6cjuw && git show FETCH_HEAD:docs/adr/0146-studio-designs-setup-administers-one-editor-per-item-paired-customize-package.md | sed -n '206,209p;396,399p;419,421p'
  2. 拒绝只针对依赖闭包内的同名,无依赖关系的两个包照旧各自共存。re-check:同一文件 grep -n "bears a" ,D2 用词是 "bears a base's item name"。
  3. 现网有多少「依赖包存了基础包同名项」的记录,未实测(只在 showcase 上人为复现过)。N-09 动手前先清点(见裁后执行)。

第一问:非配对依赖包的未声明同名项

选项 做什么 客户可感知后果 工作量
A 一律拒绝 依赖闭包里任何包(不只 .custom)用基础包已有的名字又没声明,发布时拒绝。提示两条出路:要改基础包,就到 .custom 声明覆盖;不是要改,就换个名字。D8.2「按自己地址读取」和 D9.1「遮蔽并告警」两句删掉 「移动外勤」的作者发布时当场看到拒绝和出路。基础应用永远不会被别的包悄悄顶替 ADR 改三句 · 同一个 PR · N-09 实现时多一个判断条件
B 只拒配对包 只有 .custom 的未声明同名被拒。其他依赖包可以合法用同名,算它自己的项,只在点名它的包时读到。D2 里「或任何依赖包的项」删掉 「移动外勤」发布成功,但基础应用不受影响(靠 #28238 A 的读取规则)。作者以为自己改了基础视图,其实只是新建了一个同名视图,没有任何提示 ADR 改两句 · 同一个 PR · 读取规则长期承担区分责任

业务含义直译

  • A:公司里一个工号只对应一个人。分公司想换掉总部的某个流程,必须走正式的定制申请(.custom 声明);不是要换,就另起名字,不许冒用总部的工号。
  • B:允许分公司用总部的工号登记自己的人。总部名册不受影响,但同一个工号在不同名册里指不同的人,查起来要先问「你看的是哪本名册」。

四维分析(业务立场)

推荐:A。 只看 ① 选 A;②③④ 是否翻转:否。不取 B:它让 #28238 那类记录合法化,AI 写错时静默通过,而且依赖链上的名字从此要靠「看哪本名册」才能说清。

置信缺口:现网存量未清点。如果有客户有意在依赖包里复用了基础包的名字,A 会让这些包下次发布时被拒,需要改名或改到 .custom 里声明。先清点再上线可以兜住这一点,见裁后执行。

第二问:action 在本 epic 里能否覆盖

选项 做什么 客户可感知后果 工作量
A 本 epic 不开放 action 从 D2 的可覆盖清单移到「不在本 epic」,和动作开关一样延后。客户需要新按钮时,在 .custom 里新增一个 不能替换厂商的按钮,只能新增自己的 ADR 改一句
B 开放,附条件 和 flow 一样写清条件:替换后点击只触发一个定义,引用它的页面在一次重载内切换,等等 能替换厂商按钮的行为 ADR 加一节,N-11 多一类行为替换的实现和测试,无先例

推荐:A。 只看 ① 选 A;②③④ 是否翻转:否。理由有三:

  • 没有实测需求:本 epic 里替换动作的拉动没量过。
  • 它是行为类:替换它就是改「点了之后发生什么」,flow 为此需要四个条件,action 没有理由更宽。
  • 和已有裁定一致:「动作开关不入 epic」已经把动作这条线整体推后。

不取 B:在没有拉动的地方先付出一套行为替换规则,属于扩散。

os-decision-facets
① 长远:A 把依赖链上的同名收成唯一合法形状(.custom 声明),与改写稿 D8「一个读取有主人」一致;B 永久保留闭包内同名不同物;action 延后不增特例
② 拉动:#28238 实测即非配对依赖包顶替基础视图;有意复用基础包名字的存量未实测;action 替换无实测拉动
③ 防错:A 发布时响亮拒绝并给出 .custom 声明的出路;B 让「以为定制了其实没生效」静默通过
④ 不扩散:A 复用 Q1 已裁门禁不加能力;B 新增长期共存语义;action 开放需新增一套行为替换条件
Prior rulings read: undeclared same-name dependent package refused publish / non-pair dependent same-named item own address → 2 hits (#28237 review 6538795712, #28238 letter 6538867716); ADR-0048 §3.3, ADR-0126 §3 / §7.1; thread: fable director "总监决裁 7" (not readable from here; recommendation recorded there as "C for both" — content unknown to this card)

裁后执行

Analysis-tier: opus, desktop session (no fable re-read)


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling (maintainer os-elon-musk, fable 决裁 thread, 2026-10-31 06:48–06:56Z) — both questions decided; this card closes with #28237 carrying the text.

    Maintainer, verbatim: 「没写这行声明、又恰好同名,是否建议统一规则,要么申明覆盖,要么禁止。这样会有什么坏处吗」 → fable listed the costs → 「同意」; and on this card: 「还有一个 28364 的决裁,是不是同一件事」 → confirmed the same matter, 「28364 你也回复」.

    Question 1 — undeclared same-name item of a non-pair dependent: A, uniform

    Within the base's dependency closure, ANY dependent package's item (every overridable type, not only .custom) that bears a base item's name without the overrides: { package, basedOn } declaration is refused at publish. Riders the maintainer agreed to:

    1. The refusal names both fixes: declare the override, or rename.
    2. Scope is the dependency closure only. Two packages with no dependency relation keep ADR-0048 §3.3/§3.4 coexistence; ADR-0048 §3.4 gains the dependent-of-base exception.
    3. Any package in the closure may declare an override, not only .custom; one declarer per base item — the second publish is refused naming the first.
    4. D2 (:205–209), D8.2 (:395–398), D9.1 (:420–422) and the Q1 row of ADR-0146 say this one thing.
    5. N-09 rider (this card's): before implementing, count existing closure same-name rows in the examples and test deployments; zero → proceed, non-zero → report the list to the maintainer before choosing a migration.

    Fable's independent read, for the record: views, pages, reports and dashboards are read by address, so a same-name item in a dependent would coexist harmlessly (ADR-0048 §3.3); flows and actions are found by name at trigger time, so a same-name item is a collision (ADR-0126 §7.1, #11665). Fable therefore offered a type split (declarative = refuse only .custom; behavioural = refuse every dependent). The maintainer chose one uniform rule over the split, with the measured cost accepted: a dependent that ships a generic name the base also ships (HotCRM ships page/home, hotcrm/src/sales/pages/home.page.ts) must rename or declare. #28238's read-side ruling A stands either way.

    Question 2 — action overridable in this epic: C (Q3-shaped), which supersedes this card's A/B

    action stays in D2's type list with a condition, as flow is with D9: overridable only after the action ledger switch lands (#27842 / #27504), under D9's conditions 1–3 (served over the code package, rebound in one reload, ledger identity inherited); no card in this epic. Effect this epic is the same as this card's option A (no action override), but the ADR records the direction and its precondition instead of a bare exclusion, so a later card does not reopen the question. Fable's reasoning: admitting action now would ship an override with no switch (D4 keeps the ledger out of this epic) — a half; dropping it loses the direction.

    Seven "decided where silent" items of the draft

    Confirmed as written: 2 (basedOn recorded, never judged), 3 (uninstall refusal 409, extender family), 5 (one effective read, base via the layered code layer), 6 (migration as an idempotent per-base CLI ceremony). Changed: 1 — keep ^<major> but replace the rationale (ADR-0141 D2 runs only when installing the dependent; name what reads the range today); 4 — the criterion stays, and the whitelist IS the N-08 closed constant the write gate reads; 7 — add the install door (installPackage / marketplace local install) as the third __c enforcement point, so "a vendor's next version can never take that name" holds.

    Execution: the #28237 holder (card thread "Rewrite ADR-0146") folds the above into PR #28295, runs the delta contract review, then governed approvers and maintainer hand-merge. Fable analysis file: project files fable-director/28237-adr0146-decision-batch.md.

    Analysis-tier: ceiling


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848

    Ruled: Q1 A (uniform, five riders) · Q2 C — ruling record 6540351677 (maintainer os-elon-musk, fable 决裁 thread, 2026-10-31 06:48–06:56Z). Recorded 2026-10-31T08:15Z.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-10-31T09:36Z

    Closing as completed: the ruling (Q1 A with five riders · Q2 C, record 6540351677) is in ADR-0146 as merged by #28295 (head 04aa72e7). Closure was pre-announced in 6541041334 ("closes when #28295 merges"). Its execution riders now live on the epic (#28236 comment 6541767586) and travel with N-09. A follow-on question this ruling surfaced — rider 3's "any closure package may declare" × "one declarer per base item" — goes to its own decision card, not back into this one.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:skillspriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions