Skip to content

[finding] targetVariable 是 spec 的 declarative hint,全仓零读点(grep 实测 0 命中)—— 发布还是不发布是个未定的判断题,不是漏声明 #3834

Description

@yinlianghui

发现于 #3808 的实施过程。#3808 把它列为 C 类「已知的、不该动的」,但 C 类的正当性只在正文里说了一句,而 #3808 落的反方向 parity 门要求每条豁免引用一个 tracking issue —— 本单就是这两条豁免的落点,顺带把「零读点」这个前提用实测钉住。

按 observation-class 只贴 finding、不排队:今天不成立任何用户可见的失效(见下),请 triage 决定级别。

事实

pin 版 @objectstack/spec@17.0.0-rc.5 在两个 element 上声明 targetVariable,describe 原文(ElementTextInputProps):

Page variable this input writes to. Declarative hint; the live binding resolves via the variable whose source equals this component id (see PageVariableSchema).

objectui origin/main @ c85268256 实测:

grep -rn "targetVariable" packages/ apps/ --include=*.ts --include=*.tsx   # 0 命中

全仓零读点 —— 不是「读了但没用上」,是这个标识符在本仓一次都没出现过。真正的绑定走的是反向:usePageVariableBinding(schema?.id)(packages/components/src/renderers/basic/text-input.tsx:60)按组件 id 反查 PageVariableSchema.source,和 spec 自己的说明一致。

所以这不是 A 类缺口(渲染器实读却不可发现),也不是 showSubscriptionToggle 那种 declared-but-inert 缺陷 —— spec 自己就把它定义成 hint,行为由另一条边提供,而那条边是通的。

判断题在哪

要不要把它放进 inputs 发布出去,两个方向都有道理:

  • 发布:双向 parity 成立;作者(尤其 AI 作者)在写 element:text_input 时能在同一个面板里看到「这个输入打算写哪个变量」,是有价值的意图记录,而 PageVariableSchema.source 在页面的另一处、方向相反,不容易同时想到。
  • 不发布:它不影响任何运行时行为。发布一个「写了也不生效」的键,正是 registry inputs 与 spec ComponentPropsMap 之间没有 parity 门,4 个 block 发布了 pin 版 spec 不接受的顶层 input #3797 修的那个方向 —— 哪怕它的无效是 spec 设计如此。更糟的是它看起来像绑定的正路,一个只读 manifest 的作者很可能只写 targetVariable、不写变量的 source,然后得到一个什么都不写入的输入,零诊断。

第二条的风险是具体的,也是我倾向不发布的理由;但若发布,description 必须写明「仅为意图声明,真正的绑定靠变量的 source 反查」——即 showSubscriptionToggle 的处理方式。这是发布面语义决定,交维护者。

第三条路:上游把它退役(ADR-0049 enforce-or-remove / ADR-0087 D2 墓碑),让「意图」只有一种表达处。若 objectstack 侧本就打算收敛,本仓什么都不用做。

涉及的键

  • element:text_input.targetVariable
  • element:record_picker.targetVariable

两条形状完全相同,应一起处置。

参考位置

  • packages/components/src/renderers/basic/text-input.tsx:60(usePageVariableBinding(schema?.id) —— 真正的绑定边)
  • packages/components/src/renderers/basic/record-picker.tsx(同形)
  • apps/console/src/__tests__/registry-inputs-spec-parity.test.ts —— 反方向门的豁免名单,两条都引用本单

关联:#3808、#3797 / PR #3806、#3165(showSubscriptionToggle 先例)、ADR-0049

Activity

  1. claude commented on Aug 8, 2026

    @claude
    Contributor

    Findings round: HOLD — label unchanged, finding stays.

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


    Generated by Claude Code

  2. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings round (periodic re-verification of oldest-checked holds): HOLD maintained (finding unchanged). Re-checked @ origin/main 6b6721c: targetVariable still has zero runtime read points (hits are the spec-parity test apps/console/src/__tests__/registry-inputs-spec-parity.test.ts and CHANGELOGs; counter-probe usePageVariableBinding present in packages/components). Restart-condition watch: sibling fork #3829 is now pm:blocked (no ruling recorded on this card yet) — the pair still awaits one publish-face ruling.

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


    Generated by Claude Code

  3. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings triage: HOLD.

    Re-verified @ objectui origin/main 564252c: targetVariable still has zero code read points — the 7 repo hits are all self-referential (the registry-inputs-spec-parity.test.ts:521-532 exemption entries citing this card, plus two CHANGELOG lines); the live binding still runs the reverse edge (usePageVariableBinding). Held because publish-vs-suppress-vs-upstream-retirement is a publish-surface semantics decision for the maintainer/spec side; the parity gate's exemptions keep the state pinned and self-verifying meanwhile.

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


    Generated by Claude Code

  4. claude commented on Aug 11, 2026

    @claude
    Contributor

    Findings round (quota re-verification): HOLD maintained (finding unchanged). Re-checked @ origin/main a9a67ec: targetVariable still has zero code read points — repo hits are the parity-gate exemption block (apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:521-532, citing this card) and two CHANGELOG lines (counter-probe: grep finds those, so the zero is real). Publish-vs-suppress-vs-retirement stays a spec-side semantics decision; the parity gate keeps the state pinned meanwhile. Held.

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


    Generated by Claude Code

  5. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings cadence (triage seat): HOLD — premise re-verified on origin/main @ 6d01319: targetVariable still has zero production read points repo-wide; the only hits are apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:470-481, which now pins the zero-consumer state and documents the publish-side risk in its own comments. That pin strengthens the hold: the state is recorded and guarded, and the card's open question ("publish the hint or retire it") is a spec-side vocabulary judgment that belongs upstream, not an objectui defect. Restart condition: a spec-side ruling on the declarative-hint family, or a consumer appearing.

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


    Generated by Claude Code

  6. os-project-manager commented on Aug 17, 2026

    @os-project-manager
    Collaborator

    Triage (batch first-touch round, 2026-08-17): finding → pm:blocked — the standing hold's restart condition partially fired: sibling #3829 (the paired inert-hint card) resolved via the retirement route (PR #4794, 183d09b), which settles the route question this card was holding for. The publish-vs-retire decision for targetVariable itself is spec-surface work, so it has been filed into the enforce-or-remove channel upstream: objectstack-ai/objectstack#9198 (domain:spec, queued), which runs the ADR-0049 flow for both keys.

    Blocked-by: objectstack-ai/objectstack#9198

    What remains here after the upstream lands (either outcome): drop the parity-gate exemption block at apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:545-556 once the spec pin covers the disposition. Re-verified this round: still zero production readers in this repo.


    Generated by Claude Code

  7. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    Contributor

    Unlock fact — ⛔ not a claim, no label changed. Spec seat (objectstack), session session_01225pUjnCKWqxcc1PeqKFUq.

    This card's blocker objectstack#9198 closed 04:08Z via objectstack PR #9221, verified on objectstack origin/main: targetVariable is retired on element:text_input / element:record_picker (retiredKey() tombstones ×2, ADR-0087 retired-key entries 18.ui__ElementTextInputProps__targetVariable / 18.ui__ElementRecordPickerProps__targetVariable, D2 conversion element-input-target-variable-removed).

    What this card can now do: drop the parity-gate exemption block for these two keys (apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:545-556) — once the console's vendored spec pin advances past the retirement, the exemption is dead weight and the gate should see the keys as retired. Note the ledger verdict record and prescription live in the objectstack retired-key entries if the gate's message wants to cite them.

    Not dispatched by this seat (shift ended at the maintainer's instruction) — recorded for whoever picks this card up. Related: objectstack#9220 (element:filter's targetVariable, element-grain question, deliberately NOT covered by the retirement above — the exemption drop here must not assume it).


    Generated by Claude Code

  8. os-zhuang commented on Aug 17, 2026

    @os-zhuang
    Contributor

    Unlock scan (triage seat): pm:blocked → pm:on-hold. The blocker objectstack#9198 closed 04:08Z (PR #9221, verified by the spec seat's unlock-fact above and re-confirmed on objectstack origin/main 7ea1372), so the Blocked-by: edge is spent — but the remaining work here (drop the parity-gate exemption block at apps/console/src/__tests__/registry-inputs-spec-parity.test.ts:545-556) is not actionable yet: the retirement lives in the unpublished spec 18-line (retired-key entries 18.ui__ElementTextInputProps__targetVariable / 18.ui__ElementRecordPickerProps__targetVariable), and dropping the exemption while the console still vendors a spec without those tombstones turns the parity gate red. Requeueing now would hand a dev an impossible card, so this converts to a hold with an executable exit instead.

    Restart-when: objectui's resolved @objectstack/spec contains the retirement — check: after install, grep -rq "ElementTextInputProps__targetVariable" node_modules/@objectstack/spec/dist/ succeeds (equivalently: the lockfile resolves a spec version published after objectstack PR #9221).

    On restart, the work is exactly the spec seat's note above: drop the two-key exemption block, let the gate see the keys as retired, and do not assume anything about element:filter's key — that is objectstack#9220's separate, element-grain question (now queued upstream).

    本评论来自分诊座位 Routine — session session_01EquW1DnXShvkwiJZUfDtwi, scheduled fire ~04:36Z 2026-08-17.


    Generated by Claude Code

  9. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    on Aug 23, 2026
  10. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    Triage: domain:spec (lane only — pm:on-hold untouched). targetVariable is a spec-declared hint with zero read points repo-wide — declared-vs-enforced on a contract key, so the resolution is an ADR-0049 enforce-or-remove call, not a renderer change. packages/components is where the absence was measured, not where the question is settled.

    ⚠️ If the answer is "remove", the removal lands in @objectstack/spec (objectstack), not here — file-at-destination would move the executing card to the framework repo. Recording that rather than acting on it.

    Routed via the maintainer direct-dispatch channel, this session, verbatim: 「然后 批 4–5」. PM session session_0124Qg8rLvpXnQDwCmpKUmaJ. objectui three-stream split (maintainer 2026-08-21); not a Routine triage fire — the triage seat may re-grade.


    Generated by Claude Code

  11. yinlianghui commented on Sep 2, 2026

    @yinlianghui
    CollaboratorAuthor

    Unlock scan (spec@objectui seat, session session_01V3hPr7riucnMfhcHY86Msd, 2026-09-02): Restart-when hit, and the residual work has already landed — closing as completed.

    Readings, re-run on the merged ref rather than trusted from the hold comment:

    • Upstream: objectstack#9198 is closed, via merged PR objectstack#9221 (2026-08-17). The installed @objectstack/spec@17.2.0 (objectui pnpm-lock.yaml on origin/main 67dadd6) carries the REMOVED (#9198) tombstones on both element schemas — the retirement this card was waiting for is in the pin. The hold line's literal probe string (ElementTextInputProps__targetVariable) does not occur in the 17.2.0 dist, so that spelling was over-specific; the retirement itself is present.
    • objectui: git grep -n targetVariable origin/main -- packages ':!*__tests__*' → only two CHANGELOG lines (packages/components, packages/plugin-detail) that record the old exemption; packages/types/src/__tests__/registry-inputs-spec-parity.test.ts → zero targetVariable entries. The exemption block that was this card's only remaining scope is gone (deleted with the 17.1.0 pin bump, objectui#5328 / PR Pin @objectstack/* to 17.1.0 #5529 per the scan).

    Nothing left to dispatch. pm:on-hold removed with the close; domain:spec stays as provenance. Reopen if a read site or exemption for targetVariable reappears.


    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:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions