Skip to content

finding: the ruled submitBehavior.url token substitution + escape now exists twice (console app, plugin-form package) — the RULE is shared, the consumer mechanics are not #5035

Description

@yinlianghui

Observation-class finding, recorded while implementing objectui#4989. Nothing a user hits today: the two implementations currently agree on every value, and each is pinned against the spec's own schema. finding label, no pm:queue — this is a drift-risk note for whoever next touches either file, not a defect to schedule.

What exists twice

objectstack#7496 ruled submitBehavior.url in three parts: relative-only, {{record.field_name}} interpolation, every interpolated value URL-escaped when the redirect is built. The spec enforces part 1 at the authoring door but exports no substituter, so parts 2 and 3 are the consumer's to implement. Two consumers now have:

Why this is a note and not a defect

The part that would actually hurt to duplicate — the seven refusal families — is not duplicated. Both modules ask @objectstack/spec's own FormViewSchema at the moment of use and quote its message back, which is exactly what scripts/check-spec-symbol-derivation.mjs asks for. A spec release that widens or narrows the ruling moves both without an edit to either.

What is written twice is about 20 lines: the token regex /\{\{record\.([a-z_][a-z0-9_]*)\}\}/g, the scalar-to-string urlValue, the encodeURIComponent call, and the unresolved-brace fail-closed backstop. Both are pinned by test suites that use the live schema as the oracle on the OUTPUT (whatever is emitted must still be a value the contract accepts), so a divergence in either copy fails that copy's tests rather than shipping quietly.

The real drift surface, named

Two deliberate divergences exist already and are documented in both docblocks, so a future consolidator should know they are intentional rather than accidents:

  • Return shape. The console resolves to a path it hands to a ROUTER; plugin-form resolves to a url it hands to window.location.assign. Not cosmetic — it reflects that the console's two form mounts live inside its router while a published component library may be rendered outside one (see objectui#4989 defect 4, escalated for exactly that reason).
  • Split vs fused. The console separated the shape verdict into checkSubmitRedirectUrl because it owns an authoring door as well; plugin-form has no counterpart, since a renderer only ever consumes.

The plausible drift is therefore not "the rule diverges" but "one copy gains a refinement the other does not" — e.g. the token grammar widening upstream, or a decision about non-scalar values, applied in one place only.

Why it was not consolidated in objectui#4989's PR

A published package cannot import an app's module, so consolidating means moving the shared mechanics DOWN into a package both depend on (@object-ui/core is the natural candidate — logic only, no UI deps, and apps/console already depends on it transitively). That is a refactor spanning an app and two packages, with a public-surface question attached (does @object-ui/core export a redirect resolver?), which is well outside the scope of a card about how two components consume one key. Prime Directive #10.

If it is ever scheduled, the shape

Move urlValue + the token regex + the escape + the fail-closed backstop into one module in a shared package; keep the two return shapes as thin adapters over it (path for the router consumer, url for the browser-level one), since that difference is real. Do NOT unify the shape verdict — it is already the spec's, in both places, which is the property worth protecting.

Activity

  1. os-support-ai commented on Aug 19, 2026

    @os-support-ai
    Collaborator

    Triage (first-touch, triage seat, session session_01HXzdkKx5WjwwCTT3c7U5kB, 2026-08-19): graded pm:on-hold.

    Reason: the card itself establishes this is a drift-risk note, not schedulable work — both copies are pinned by tests that use the live spec schema as oracle, the refusal families are NOT duplicated, and consolidation is a cross-package refactor with a public-surface question (@object-ui/core exporting a redirect resolver) that would need a maintainer decision anyway. Source: card author's own analysis (filed while implementing #4989), consistent with the "temporarily-not-planned ⇒ close or hold-with-exit" discipline; held rather than closed because it has a concrete, mechanical wake condition.

    Restart-when: a dispatch or open PR's file surface touches apps/console/src/components/submitRedirect.ts or packages/plugin-form/src/submitRedirect.ts (trigger-file check at dispatch time; the toucher inherits this card as a declared add-on or re-prices it).

    If consolidation is ever actually scheduled, it must go through the maintainer for the @object-ui/core public-surface question — do not dispatch it from this hold directly.


    Generated by Claude Code

  2. added
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 23, 2026
  3. os-zhuang commented on Aug 23, 2026

    @os-zhuang
    Contributor

    Triage: domain:ui (lane only — pm:on-hold untouched). Landing: apps/console and packages/plugin-form — the ruled submitBehavior.url token substitution and escaping.

    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

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    关闭 not_planned —— 维护者同意:两份重复代码都有测试守着,一旦走偏测试会失败;合并它需要跨包改公共接口,没有需求

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T12:48Z。objectui 清理第八批(#8587 的挂起卡一次性清点):本席读完了卡面和评论(2 条,全部读到),并在本地 main(62597c5)上重新测量,把建议报给维护者。维护者答:「第⑧批 同意」。

    本卡说的是什么

    已经裁定的 submitBehavior.url 占位符替换和转义逻辑存在两份,约 20 行:一份在 apps/console 的 submitRedirect.ts,一份在 plugin-form 的 submitRedirect.ts。拒绝规则没有重复,两边都直接问 spec 的 FormViewSchema。提交时就注明"这不是要排期的缺陷",只是提醒将来可能走偏。

    本席实测(本地 main 62597c5)

    • 两个文件都在,RECORD_TOKEN_RE 完全相同(console :121,plugin-form :96)。
    • 两份代码都有测试,测试以实际的 spec schema 作为判断标准。哪一份走偏,哪一份的测试就会失败。
    • 重启条件 5337041552(有派发任务改到其中一个文件)没有发生。卡面也说明,要合并成一份需要维护者决定是否动 @object-ui/core 的公共接口。

    为什么关

    没有用户影响;走偏的风险已经有测试兜底;合并需要跨包重构并涉及公共接口,目前没有需求推动。

    什么时候重开

    两份代码真的出现了差异;或者上游 spec 改了占位符语法,两边都要改时,那时一并考虑合并成一份。

    标签:摘 pm:on-hold;以 not_planned 关闭。


    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:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions