Repository navigation
[Decision] ADR-0087: a changeset that retires published runtime exports and carries a FROM → TO block has no honest disposition category: a seventh category (A), a registered D3 entry (B), or a dated reading plus a looser detector (C)? #22915
Description
Ruled: 6116351873 · letter A · 2026-10-12T01:53Z
Filed by the domain:services seat 2 (seat post #21118, session_013LbZ9MhPp1iriZEtgJqioA) at the maintainer's request in session chat (「ADR-0087 的分类缺口,开决策卡?」), from the at-tier contract reviews on PR #22875 (6113872462, 6114282872). Filing gate ②: only the maintainer can make this call. Every option either edits ADR-0087 (governed text) or changes what check-adr-0087-registration admits, and #13080's triage placed exactly that class in the decision box. ⛔ Not a claim.
维护者速读
- 事情: 一个包删掉已发布的运行时导出(函数、常量、插件选项)时,changeset 会给使用者写一段「旧名字 → 改用什么」的迁移列表。ADR-0087 要求每份声明破坏性变更的 changeset 选一个处置分类,现有六类里没有一类能如实对上这种情况。
no-migration-prescription按 ADR 原文,遇到带迁移列表的正文就要拒。runtime-interface-only只认 HEAD 上还存在的接口/类型,删掉的导出不在了,而且它同样遇到迁移列表就拒。- refactor(plugin-email): templates resolve the registry; the seed and the provenance stamp retire; organization-level editing is closed (ADR-0131 D6/D10) #15205 两个阶段的 changeset 都只能挂
no-migration-prescription。门禁能放行,只是因为检测器没认出没有箭头的列表行。D7 补充条款写过,这种「靠检测器漏判撑着」的豁免正是要避免的。
- 选项:
- A: 新增第七类
runtime-export-retired,带可复查的谓词。 - B: 这类变更改走
registered,每次写一条 D3 迁移条目。不改 ADR、不改门禁。 - C: 只在 ADR 附录写一句读法,并让检测器不把只涉及代码导出的迁移列表算作处方。
- A: 新增第七类
- 席位推荐 A,退而求其次 B。 请回一个字母:A / B / C。
One-sentence problem
When a package retires published runtime exports and its changeset tells consumers what to call instead (a FROM → TO block), ADR-0087 offers no disposition category that states this honestly. The catch-all no-migration-prescription holds today only because the gate's prescription detector misses list lines that carry no arrow.
Background (measured)
- ADR-0087's six categories (addendum at
docs/adr/0087-metadata-protocol-upgrade-contract.mdnear:727–:742, onmain):registered,unpublished,already-registered,no-migration-prescription,runtime-interface-only(D7), andtype-surface-only(the sixth, added 2026-08-30 by [finding] check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — every category is refused, so the class is pushed to drop the**BREAKING**token instead #13080). The addendum's own words: "no-migration-prescriptionis refused by a body that carries a migration prescription". D7 adds thatruntime-interface-only"inherits theno-migration-prescriptionrefusal". - D7's own rationale fits this case: a compiler-carried change "needs no ledger entry", because "the channel that actually reaches every affected consumer is the compiler". D7 named a category precisely because the earlier exemption "was held by a detector miss rather than by a positive finding".
- D7's predicates do not fit it:
runtime-interface-onlyrequires each named<path>#<Symbol>to resolve at HEAD as an exportedinterface/type/class/enum. A retired function, constant or plugin option does not exist at HEAD. And the inherited refusal bars any body with a FROM → TO block. - The worked example: PR feat(plugin-email)!: email templates resolve from the registry; the built-in seed, the boot sweep and the live projector retire (ADR-0131 D6/D10, C4 stage 2) #22875 (refactor(plugin-email): templates resolve the registry; the seed and the provenance stamp retire; organization-level editing is closed (ADR-0131 D6/D10) #15205 stage 2, merged as
7de0265fc7) retires nine exports and two plugin options of@objectstack/plugin-email. Its changeset carries a six-line FROM → TO block and holdsnot-required (no-migration-prescription);check-adr-0087-registrationexits 0. The at-tier review (6113872462) found that the gate admits it only because the list lines carry no arrow, which its detector requires. Stage 1's changeset (15205-email-template-org-door-closed.md) holds the same category. - How common it is, measured on
mainat this filing:- 80 pending changesets declare BREAKING and hold
no-migration-prescription. Most are behaviour changes with no FROM → TO block, which the catch-all fits honestly. - 7 carry FROM/TO wording. Four are console-pin bumps (pin FROM → TO). The clear member of this class is
15205-email-templates-resolve-registry.md;21908-principal-less-strict-mode.mdand22785-import-user-field-target.mdare candidates, not read. - The ADR-0131 v18 retirement batches keep retiring runtime exports, so the class recurs.
- 80 pending changesets declare BREAKING and hold
Options × real cost × workload
| Option | What it does | What an author sees | Workload |
|---|---|---|---|
| A | A seventh category, not-required (runtime-export-retired <package>#<name>[, ...]), with re-runnable predicates. Each named export is reachable from the package's published entry at the merge base and absent at HEAD. None is a metadata surface (not *.zod.ts, not packages/spec/src/contracts/**, not an object definition) and no metadata surface references it at the base. A FROM → TO block is admitted only when its FROM operands are exactly the named exports. |
A retirement changeset names what it removed and keeps its migration block; the gate verifies the claim. | domain:devx, one PR touching docs/adr/** (governed, so a human merge) plus gate predicates and self-tests. Precedent: #13080's sixth category, PR #13596 (+956 / −10). |
| B | No new category. A retirement that carries a FROM → TO block registers an ADR-0087 D3 semantic entry (registered <id>), so os migrate meta and the upgrade guide carry the prescription. |
One extra entry file per such changeset (about 40 lines); the upgrade guide gains host-code migration lines beside metadata ones. | None now; per changeset afterwards. No ADR or gate change. |
| C | A dated reading in ADR-0087's addendum: a FROM → TO block that names only package exports or plugin options is not a migration prescription in ADR-0087's sense. The detector stops counting such lines. | Authors keep no-migration-prescription; the gate admits by rule instead of by miss. |
domain:devx, one PR touching docs/adr/** (human merge) plus a detector change. |
⛔ Not offered: the status quo. Keeping the catch-all on a detector miss is the shape D7 was written to retire. The next retirement whose list lines carry an arrow gets refused, and its author is pushed to drop the block or the BREAKING banner: the failure #13080 recorded.
Four axes (from the business side)
- 项目长远合理性: A 延续 D7 和 [finding] check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — every category is refused, so the class is pushed to drop the
**BREAKING**token instead #13080 的做法:每一类「编译器能带过去的变更」都有一个可复查的正面判据,门禁读事实,不读作者的保证。B 把宿主代码的迁移塞进本是给元数据作者看的迁移账本,和 D7「编译器比账本条目更精确,不需要账本」的已批推理相反。C 用一句散文放宽检测器,判据不可复查。 - 实际业务需求: 拉动是实测的,但面窄。refactor(plugin-email): templates resolve the registry; the seed and the provenance stamp retire; organization-level editing is closed (ADR-0131 D6/D10) #15205 两个阶段都撞上了,v18 的退役批次(ADR-0131)还会继续删运行时导出。80 份挂兜底类的破坏性 changeset 里,确定属于这一类的只有 1 份、疑似 2 份。
- 防 AI 犯错: A 下作者(或代写的 agent)点名删了什么,门禁逐个核对,写错名字会被拒。C 下只要写的是「代码导出」就放行,写错了没人发现。B 能核对条目 id,但不核对条目内容是否覆盖这次删除。
- 创业阶段不扩散: B 现在零成本,不加门禁复杂度;A 给现有门禁加一类和一组谓词(不是新门禁),约千行,有先例;C 改动最小,但放宽的是门禁。
Recommendation, fallback, confidence gap
- Recommended: A. Fallback: B.
- On ① alone, A: it is the same move D7 and [finding] check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — every category is refused, so the class is pushed to drop the
**BREAKING**token instead #13080 made for the two neighbouring classes. It is a named category resting on a positive, re-runnable finding, so a compiler-carried retirement needs no ledger entry and no detector miss. - Confidence gap: the class is measured narrow. If roughly a thousand lines of gate code is out of proportion for a class this size, B is the correct cheap path: it uses an existing verified mechanism and weakens nothing.
- 自检:只看①选 A;②③④是否翻转:否(② 面窄,偏 B 但不翻转;③ 偏 A;④ 偏 B,成本差是确定的,但 B 的代价是账本里混入宿主代码条目)。
After the ruling
- A: a
domain:devxcard implements the category: ADR-0087 addendum, gate predicates, self-tests and the worked example. Governed PR, human merge. Existing changesets are not re-graded; new ones use the category. - B: the ruling is recorded on this card, and the next retirement changeset registers a D3 entry. The pending
15205-*changesets stay as they are, since they ship before any rule change takes effect. - C: a
domain:devxcard writes the dated reading and the detector change. Governed PR, human merge. - In every case this card closes as its decision record.
os-decision-facets
① 项目长远合理性:A 沿用 D7/#13080 的「点名 + 可复查谓词」;B 让迁移账本混入宿主代码条目,与 D7 推理相反;C 以散文放宽检测器。
② 实际业务拉动:#15205 两个阶段实测撞上;v18 退役批次会再遇到;但 80 份兜底 changeset 里确定只有 1 份属于这一类。
③ 防 AI 犯错:A 逐个核对点名的导出,写错即拒;C 写了就放行;B 只核对条目 id,不核对内容。
④ 创业阶段不扩散:B 零成本,不加门禁复杂度;A 给现有门禁加一类(约千行,有先例);C 改动最小但放宽门禁。
Prior rulings read: D7 (#8299, PR #8456, the runtime-interface-only category); #13080's ruling 5468992520 (the sixth category type-surface-only, maintainer 「同意」 2026-08-30, PR #13596); #17357 (the detector's heading-denial fix); thread: PR #22875's reviews 6113872462 and 6114282872.
Dedupe (MCP search_issues, closed cards included):
- "ADR-0087 disposition category not-required no-migration-prescription runtime exports removed FROM TO block changeset vocabulary gap" → 93 hits. The same family, all closed: ADR-0087's changeset disposition has no category for published runtime TS interfaces with no metadata surface — the #8277 exemption argument is correct, unverifiable, and will be re-litigated #8299, [finding] check-adr-0087-registration has no honest disposition for a published TYPE-surface narrowing — every category is refused, so the class is pushed to drop the
**BREAKING**token instead #13080,check-adr-0087-registrationreads a heading that DENIES a migration prescription as evidence of one, sonot-required (no-migration-prescription)is unclaimable by the case it names #17357, check-adr-0087-registration: a## Migrationsection loses its framing at the first###sub-heading, so its rewrite tables read as no prescription #12996. None covers removed runtime exports carrying a FROM → TO block. - "check-adr-0087-registration detector arrow migration prescription runtime-interface-only type-surface-only category" → 10 hits, all closed, none this question.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 12, 2026
Ruling: batch #319 item 1 · letter A · maintainer 「22915 同意」 2026-10-12T01:53Z
Director seat, summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB(GitHubos-zhuang; written asobjectstack-fleet[bot]via the relay). Batch #319 (two cards, this one first) was presented in chat with this seat's own readings and the recommendation "A, fallback B"; the maintainer answered 「22915 同意」, which takes the recommendation. Freshness gate: the body re-read before this record; the card carried no comment. Thread-read: none.Premises re-run on
origin/main(7dbb89dc) in this act: the ADR-0087 addendum's closed vocabulary lists exactly six categories,no-migration-prescription"is refused by a body that carries a migration prescription", andruntime-interface-only"inherits theno-migration-prescriptionrefusal"; D7 (:747) names the compiler as the channel and records that the earlier exemption "was held by a detector miss rather than by a positive finding"; PR #22875 merged as7de0265fc7with15205-email-templates-resolve-registry.mdcarrying a FROM → TO block underno-migration-prescription; 81 pending changesets declare BREAKING and holdno-migration-prescription(the card counted 80 before #22875 landed).The ruling
A: ADR-0087 gains a seventh disposition category for a changeset that retires published runtime exports and carries a FROM → TO block, resting on re-runnable predicates.
<!-- adr-0087: not-required (runtime-export-retired <package>#<name>[, ...]) <why> -->.*.zod.ts, notpackages/spec/src/contracts/**, not an object definition) and no metadata surface references it at the base; a FROM → TO block is admitted only when its FROM operands are exactly the named exports. The category inherits nothing it cannot prove: a body whose FROM operands name anything else keeps theno-migration-prescriptionrefusal.runtime-interface-onlyandtype-surface-only, with the worked example (15205-email-templates-resolve-registry.md) and its measured history, as the two neighbours have.os migrate metaand the upgrade guide carry, against D7 / D8's reasoning that the compiler, not the ledger, is the channel for a TypeScript consumer); C (a prose reading that a FROM → TO block naming only package exports is not a prescription, with the detector relaxed to match: the gate would admit by the author's word, the shape D7 was written to retire).Why on the first axis
ADR-0087's vocabulary is closed and every exemption rests on a positive, re-runnable finding; D7 and #13080 added the two neighbouring classes exactly this way. A compiler-carried retirement of published exports is a third such class, and it recurs with ADR-0131's v18 retirements. 自检:只看①选 A;②③④ 是否翻转:否(② 面窄:确定 1 份、疑似 2 份;③ A 逐个核对点名的导出,写错即拒;④ 偏 B,成本差确定,但 B 的代价是迁移账本混入宿主代码条目)。A is more than 3× the cost of B; the first-axis readings are not close (B contradicts D7 / D8), so the letter stands.
Workload (#22597's rule):
domain:devx· 1 PR touchingdocs/adr/0087-metadata-protocol-upgrade-contract.md(governed, Tier H: the maintainer's APPROVED review, then the owning seat lands it) plusscripts/check-adr-0087-registration.mjspredicates and self-tests · precedent #13080's sixth category, PR #13596 (+956 / −10). Confidence gap: the class is measured narrow (one certain member among 81); the predicate "reachable from the package's published entry at the merge base" needs the dev to pick the resolver the api-surface gates already use rather than a new one.Prior rulings read: ADR-0087 D7 (#8299, PR #8456) and D8; #13080's ruling 5468992520 (
type-surface-only, PR #13596); #17357;check-prior-rulings.mjs --card 22915in this act: 203 hits, ADR-0087 D3 / D7, ADR-0131 D13 / D5, ADR-0059 §3, ADR-0076 D12, ADR-0081 §2, ADR-0120 D7, ADR-0002 §6 named, none answering this question; PR #22875's reviews 6113872462 and 6114282872; thread: none.State and execution
completedin this act as its decision record, as its own text prescribes;needs-user-decisionleaves with the close. TheRuled:line is added to the body.domain:devxtoolingcard inpm:queuewhose first line names the published surface the gate protects (the ADR-0087 disposition every BREAKING changeset ships to consumers), carrying the predicates above, the worked example, the Tier H landing and the+956 / −10precedent; its number is recorded in this card's closing comment. Thedomain:devxlane claims and dispatches it; the ADR edit waits on the maintainer's APPROVED review before the seat lands the PR.objectstack-fleet commentedon Oct 12, 2026
Closed
completedon ruling 6116351873 (batch #319 item 1 → A, the maintainer's word verbatim 「22915 同意」): this card is the decision record; the implementation card is #22918 (tooling·domain:devx·pm:queue, first line naming the published surface the gate protects), filed by the director seat on the maintainer's word. Director seat, summon #37,session_01LqNgdPZB2Jnr5HpGbaqLeB, 2026-10-12T01:54Z.