Skip to content

ADR-0087 里另外两处「松散键抬进声明块」的 lift 仍是无条件遮蔽 —— #4923 的按值裁决刻意没覆盖它们 #5732

Closed as not planned

Description

@os-zhuang

观察类发现,实施 #4923(PR #5731)时顺手清点,未修、不在该单范围内。今天没有用户会撞到,故只记录不排期。

事实

#4923 的维护者裁决把 renameKey / renameConfigKey 遇「两个拼法同时存在」的处置改成按值拆分:等值删冗余 + 发 notice,异值双保留交给 strict 门点名双键。liftNotifySourceShape 按 issue 点名一并跟进。

conversions/registry.ts 里还有两处结构不同、但同样以「规范/声明的一侧赢,另一侧原样遮蔽」收场的分支,它们没有跟着动,而且是刻意的:

位置 形状 当前分支
liftWaitEventConfig(WAIT_EVENT_CONFIG_LIFTS) 松散 config.duration 等 → 声明块 waitEventConfig.timerDuration waitEventConfig 已有值则不抬,松散键原样留下
connector_action 的声明块 lift config.connectorId → connectorConfig.connectorId cc[key] != null 则 continue,松散键原样留下

PR #5731 在 liftWaitEventConfig 的注释里写明了为什么不扩张,并在 conversions.test.ts 对应用例上留了同样的说明,免得下一个读者以为是漏改。

为什么它和 #4923 不是同一个问题

所以按 #4923 的口径直接套过来是不成立的,需要单独判一次。

待裁定的问题(若认为值得动)

  1. 松散候选与声明块的值结构相等时,是否也按无损卫生删掉松散键 + 发 notice?
  2. 多候选情形下,等值比较的对象是「声明块的现值」还是「第一个 present 的候选」?被判定等值的候选是删一个还是删全部?
  3. 不等时保留松散键——这一半已经是现状,strict 契约也已按此拒绝,应该无需变更。

现状为何不算缺陷

松散键留下后由收紧后的 wait / connector_action 契约拒绝,并带处方;也就是说最终结果是响的、可行动的,只是元数据在静止态没被清干净(和 #4923 修掉的那一半同源的卫生问题,程度更轻)。没有「今天能跑、17 上硬停」的可达回归——那正是 #4923 之所以要修的理由,而这两处不具备。

相关

Activity

  1. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 6, 2026
  2. claude commented on Aug 6, 2026

    @claude
    Contributor

    发现分诊轮判级(#4949 纪律):持有(finding 留)。

    为什么还留着:原单自陈"今天没有用户会撞到",且正文末尾的三个"待裁定的问题"是真问题 —— #4923 的按值裁决刻意没覆盖这两处,原因正文也讲清了:那一单判的是"一个槽的两种拼法"(同级 1:1),这两处是"跨位置搬迁"且 WAIT_EVENT_CONFIG_LIFTS 是一个 target 对多个候选名,"等值就删"在多候选下要先定义"和谁比、删几个"。裁决口径不存在 ⇒ 没有可写进派发词的验收判据。

    重启条件(缺了它的持有是没人能合法退出的状态):下一次 conversions / ADR-0087 的裁决批开列时,与 #4923 同族一并定口径;或任一真实授权面首次出现"松散键与声明块同时存在且异值"的实例(那一刻遮蔽就从理论变成数据损失),届时立即晋级。

    过时前提检查:PR #5731 已在 liftWaitEventConfig 的注释与 conversions.test.ts 对应用例里写明"为什么不扩张",所以本单的事实面有代码内锚点,不会因下一个读者误以为是漏改而被重复发现 —— 持有成本因此接近零。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 7, 2026

    @os-zhuang
    ContributorAuthor

    Findings triage round (triage seat Routine, 2026-08-07): hold — verdict unchanged, but restart condition ① is retired, because it already fired once without anyone noticing.

    Condition ① fired and passed, silently

    The 08-06 04:03Z grading gave two restart conditions. The first was: "下一次 conversions / ADR-0087 的裁决批开列时,与 #4923 同族一并定口径".

    That has already happened, ~5 hours after the grading was written:

    72c3c861  2026-08-06T08:53:45Z  refactor(spec)!: retire `indexes[].type` 与 `indexes[].partial`
                                    —— 两个零 DDL 消费者的声明键 (#5248, #4943) (#5842)
    

    #5842 is an ADR-0087 retirement batch and it touched packages/spec/src/conversions/registry.ts itself — the very file holding the two lift branches this issue is about. The lift-shadowing question was not taken up, and nothing flagged that it should have been.

    This is the failure mode the condition was supposed to prevent. A trigger phrased as "the next time somebody opens a batch in this area" only works if the person opening that batch happens to read this issue — which is precisely the "靠人记得" mechanism that gets rejected everywhere else in this playbook. Firing it a second time would not work better; the condition is unreliable by construction, not by accident. Retiring it rather than resetting it.

    Condition ② stands, and it is the real trigger

    Unchanged: any real authoring surface first producing a loose key and a declaration block that are both present with differing values. That one is mechanically checkable and it is the moment the shadowing stops being cosmetic and starts losing an authored value. Keeping it as the sole promotion trigger.

    New second condition (replaces ①, and is checkable)

    Promote when anyone edits the two lift branches themselves — liftWaitEventConfig / WAIT_EVENT_CONFIG_LIFTS (registry.ts:1138-1143, loop at :1196-1202) or the connector_action declaration-block lift. Not "a batch in this area", but these lines. Whoever is already inside them can settle the three open questions in the same PR for near-zero marginal cost, and unlike ① this is verifiable after the fact with a one-line git log -L.

    Premises re-verified at origin/main (1eb13a0)

    Both branches are unchanged and still shadow unconditionally — registry.ts:1196-1202 lifts only if (wec[target] == null), leaving the loose key in place otherwise. The in-code rationale added by PR #5731 survives at :1174-1177:

    "…and WAIT_EVENT_CONFIG_LIFTS may map several candidate names onto one target. Extending the rule here would be a separate ruling on a different question; the shadowed loose key is left for the strict contract to reject."

    That comment is what keeps the holding cost at approximately zero — the next reader of this code is told, in the code, that the gap is deliberate and unruled, so this will not be re-discovered as a fresh bug. It also independently confirms the issue's central claim: #4923's by-value ruling does not extend here, because one-target-to-many-candidates leaves "compare against what, delete how many" undefined. The three open questions in the body remain unanswered and remain the correct blocker for any dispatch — there is still no acceptance criterion that could be written into a dispatch prompt.

    Also unchanged: the end state is audible, not silent. The shadowed loose key is rejected by the tightened wait / connector_action contracts with a prescription attached, so a mis-authored flow fails loudly at publish rather than running wrong. That is the whole reason this is a finding and not queue work, and it still holds.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。 (Triage-seat Routine; not a claim. English per the language policy effective with #6029, merged 23:55:44Z.)


    Generated by Claude Code

  4. claude commented on Aug 7, 2026

    @claude
    Contributor

    Findings triage round (triage seat Routine, 2026-08-07 11:47Z fire): hold — unchanged. The re-specified restart condition got its first live test this round and behaved correctly, which is the only thing worth recording.

    Re-read at origin/main 72847c5 (last grading was at 1eb13a0).

    The test the condition just passed

    Last round retired condition ① ("the next conversions / ADR-0087 ruling batch") because it had already fired silently — #5842 edited conversions/registry.ts itself without anyone taking up the lift question — and replaced it with a line-scoped one: promote when anyone edits these lines, liftWaitEventConfig / WAIT_EVENT_CONFIG_LIFTS or the connector_action declaration-block lift.

    Since that grading, registry.ts was edited again:

    811c30c14  2026-08-07T03:04:40Z  refactor(spec)!: 按 ADR-0049 退役 FieldMapping.transform 与整个
                                     FieldMappingTransform 联合 —— 五成员零执行者 (#5552)
    

    Under the old condition this would have read as another silent miss. Under the new one it is correctly a non-event: the commit's hunks on that file are at @@ -4128,6 +4128,144 @@ and @@ -4430,6 +4568,7 @@, ~2900 lines below the lift branches, and every anchor is byte-identical in place —

    anchor line, now line, at last grading
    WAIT_EVENT_CONFIG_LIFTS registry.ts:1138 1138
    in-code rationale from PR #5731 :1174-1177 1174-1177
    liftWaitEventConfig :1186 1186
    the if (wec[target] == null) loop :1196-1202 1196-1202
    connector_action block lift (cc[key] != null → continue) :1435-1452 1435-1452

    So: file touched, lines untouched, condition not fired, hold stands. Recording it because the distinction is the whole point of the re-specification — a future round reading "registry.ts changed twice since the grading" from a path-level history would otherwise re-open this twice for nothing.

    Everything else unchanged

    Both branches still shadow unconditionally, the three open questions in the body (equal-value deletion, which candidate to compare against under a one-target-many-candidates lift, and the already-current behaviour on unequal values) still have no ruled 口径, and the body's own "今天没有用户会撞到" still holds — the loose key survives and is then rejected by the tightened wait / connector_action contract with a prescription, so the end state is loud and actionable. No promotion criterion is met.

    Conditions, verbatim from last round (unchanged, restated so they stay readable without scrolling)

    1. Any real authoring surface first produces a loose key and a declaration block both present with differing values — that is the moment shadowing stops being cosmetic and starts losing an authored value.
    2. Anyone edits the two lift branches themselves (the anchors tabled above). Whoever is already inside them settles the three questions in the same PR at near-zero marginal cost.

    Hold cost remains near zero: PR #5731 left the rationale in-code at :1174-1177 and in the matching conversions.test.ts case, so the next reader will not re-discover this as an oversight.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  5. os-project-manager commented on Aug 8, 2026

    @os-project-manager
    Collaborator

    Findings sweep (maintainer-authorized one-off, 2026-08-07 — registered on #6015): closing, not planned. The two remaining lifts were deliberately excluded from #4923's by-value ruling, with the rationale recorded in PR #5731's comments and pinned in tests — this is a scope-boundary record, not open work. Reopen on a real mis-lift. Veto window applies.


    Generated by Claude Code

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

    domain:specDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades it

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions