Skip to content

runner:带 query 的导航路径不可路由,且 pushState 与 popstate 对 currentPath 的口径不一致(休眠) #3595

Closed as not planned

Description

@yinlianghui

越界发现,记录于 #3578 / PR #3594(让站内导航保留 query string)期间。未在该 PR 中修改 —— 它的文件面只有 handleNavigate。今天没有任何元数据触发这条路径,故按观察项归档,不加 pm:queue。

事实(origin/main @ 632c07c,file:line)

packages/runner/src/App.tsx 里,「路由」这个概念有两个互不一致的取值口径:

  1. 前进方向(handleNavigate,App.tsx:73-77)把导航项给的整个字符串塞进 currentPath:

    setCurrentPath(to);   // to 来自 LayoutRenderer.tsx:104-105 的 item.path
  2. 后退方向(popstate handler,App.tsx:80)只取 pathname:

    const onPopState = () => setCurrentPath(window.location.pathname);

而 currentPath 会被直接拼进元数据文件路径(App.tsx:91 → lib/MetadataLoader.ts):

  • NetworkLoader.loadPage(MetadataLoader.ts:88-90):fetch(${baseUrl}/pages${jsonPath}.json)
  • LocalBundleLoader.loadPage(MetadataLoader.ts:36-46):../app-data/pages/${normalizedPath}.json

后果(两条,都只在导航项 path 自带 query 时出现)

  • 不可路由:path: '/customers?tab=open' 会去请求 pages/customers?tab=open.json,必然 404 —— 页面落到 Page not found。也就是说 runner 的路由器根本不接受带 query 的路径,尽管 item.path 在类型上是随便一个字符串(LayoutRenderer.tsx:57 的 item 是 any)。
  • 前进/后退不对称:同一个导航项,点进去时 currentPath 是 /customers?tab=open,浏览器后退再前进回来时(popstate)变成 /customers —— 两次得到不同的 loadPage 入参。

为什么算休眠

git grep '"path": *"[^"]*?' 在 examples/apps/packages 下零命中 —— 本仓的示例元数据没有任何导航项的 path 带 query,所以今天没有用户会撞上。PR #3594 只是在 URL 拼接侧显式处理了这个形状(避免拼出 path?a=1?b=2 的畸形 URL),路由侧仍然无法路由它,读代码的人容易被这个不对称绊一下。

可选方向(不预设结论)

  1. 明确不支持:handleNavigate 里把 to 规范化成 pathname 再 setCurrentPath,与 popstate 口径统一;导航项自带的 query 只体现在地址栏,不参与页面解析。改动最小,前进/后退立刻一致。
  2. 明确支持:loadPage 前先剥掉 query(两个 loader 各一处),query 交给页面自己消费 —— 但 runner 目前没有把 query 传给页面的机制,等于要先定义一个新契约。
  3. 在契约层堵住:给导航项的 path 定一个不含 query 的 schema,在创作/发布期拒绝,而不是让渲染端容忍。

方向 3 与本仓「fix the metadata, not the renderer」(AGENTS.md #0.1)最一致,但它牵涉 app.menu 的 schema 归属(@objectstack/spec 还是本仓),不是本记录能定的。

Activity

  1. claude commented on Aug 7, 2026

    @claude
    Contributor

    Findings triage (#4949 discipline) — triage seat Routine trig_01XhwLupWiUBp7GUEigK1RFW, 19:47Z fire. This card had never been graded.

    Verdict: HOLD — finding retained.

    Stale-premise check (origin/main @ 0fcd571): the asymmetry is intact, though the line numbers in the body have moved — setCurrentPath(to) is now packages/runner/src/App.tsx:121 (body says :73-77) and the popstate handler setCurrentPath(window.location.pathname) is now :126 (body says :80); useState(window.location.pathname) at :83. Re-anchor from those positions, not from the body. Dormancy re-verified the same way the body did it: no navigation item in examples/, apps/ or packages/ declares a path carrying a query.

    Why hold. Two reasons, and the first alone would be enough. (a) It is unreachable today by measurement, not by assumption. (b) The remedy is a semantics choice the card explicitly declines to pre-empt: normalise to down to a pathname (the router openly does not support query strings — cheap, honest, and matches what loadPage can actually consume) versus teach the router to carry a query (larger, and only worth it if runner pages are ever meant to be query-parameterised). A passing PR should not settle that, and PR #3594 correctly did not.

    Restart conditions (either one promotes it, and the first is the one to watch):

    1. Any navigation item's path acquires a query string in repo metadata, an example, or a customer-facing template — at that moment it stops being dormant and becomes a 404 on click plus a forward/back mismatch;
    2. The runner's routing is next touched for another reason — then the direction gets decided in the open, in a card that owns App.tsx's path handling, instead of being inherited by whoever happens to be nearby.

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


    Generated by Claude Code

  2. os-zhuang commented on Aug 9, 2026

    @os-zhuang
    Contributor

    Findings triage (re-verification round) — HOLD confirmed, finding retained.

    Premise re-checked on origin/main @ cdc0e44: both anchors intact exactly where the 08-07 grading re-anchored them — setCurrentPath(to) at packages/runner/src/App.tsx:121, popstate setCurrentPath(window.location.pathname) at :126. Dormancy re-measured: git grep '"path": *"[^"]*?' across examples/apps/packages returns zero hits (exit 1; counter-checked with a known-present neighbour pattern to rule out a broken scan). Neither restart condition has fired: no navigation item carries a query string, and the runner's path handling has not been touched for another reason. Hold stands unchanged.

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


    Generated by Claude Code

  3. claude commented on Aug 10, 2026

    @claude
    Contributor

    Triage (findings pass): held, premise re-verified on origin/main @ c29ceff — grade unchanged, no ownership taken.

    • Anchors have shifted lines but not shape: forward navigation still stores the whole string (App.tsx:121 setCurrentPath(to)), popstate still takes only pathname (App.tsx:126) — the two-reading asymmetry and the query-path 404 remain latent.
    • Still dormant: no shipped metadata declares a nav path with a query string, so no user reaches either consequence today.
    • Restart condition unchanged: first metadata that puts a query in item.path, or a runner-routing card that opens App.tsx's path handling for any other reason.

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


    Generated by Claude Code

  4. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings round — held (finding kept), premise re-verified.

    • Re-checked @ objectui origin/main e638531: both anchors intact — forward navigation stores the whole string (packages/runner/src/App.tsx:121 setCurrentPath(to)), popstate takes only the pathname (:126), so the query-path 404 and the forward/back asymmetry remain latent.
    • Held: still dormant — no shipped metadata declares a nav path carrying a query string, so no user reaches either consequence today; the direction choice (normalize-to-pathname vs make query paths routable) stays open.

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


    Generated by Claude Code

  5. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings round — held (finding kept), premise re-verified.

    Re-checked @ objectui origin/main b42558a: both anchors intact in packages/runner/src/App.tsx — forward navigation stores the whole string (setCurrentPath(to) right after the withPreservedQuery pushState), popstate takes only window.location.pathname — so the query-path routability gap and the forward/back asymmetry remain, and remain dormant: no shipped metadata declares a nav path carrying a query string. Held on that same dormancy.

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


    Generated by Claude Code

  6. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings triage (re-verify @ origin/main f812de6): HOLD — the asymmetry is intact: packages/runner/src/App.tsx:121 still stores the whole navigation string (setCurrentPath(to)) while the popstate handler at :126 takes window.location.pathname only. Note for future readers: the #3594 family's navigation test (App.navigation.test.tsx) covers the URL-assembly side (query preserved in the address bar) — it does not route query-bearing paths, so this card is NOT overtaken by it. Still dormant (no authored nav path carries a query in-repo). The three-way fix direction remains a design pick, with direction 3 touching app.menu schema ownership. finding stands.

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


    Generated by Claude Code

  7. claude commented on Aug 11, 2026

    @claude
    Contributor

    Findings cadence re-check @ origin/main 90e792e: HOLD (finding stays). packages/runner/src/App.tsx:83 still seeds currentPath from window.location.pathname only — query-carrying paths remain unroutable and the pushState/popstate asymmetry stands. Dormant (runner surface).

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


    Generated by Claude Code

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

    @os-project-manager
    Collaborator

    Triage (batch first-touch round, 2026-08-17): finding → pm:on-hold — state transition of the standing 2026-08-07 HOLD (fully dormant: zero authored nav paths carry a query; the only ?-path is a test fixture). Premise re-verified this round at App.tsx:119 (raw setCurrentPath(to)) vs :124 (popstate pathname-only). The exit is a design pick (normalize-to-pathname vs query-routable vs schema-level refusal), priced as design + fix.

    Restart-when: git grep '"path"' -- examples/ apps/ packages/ finds an authored nav path containing ?, or a PR touches packages/runner/src/App.tsx routing


    Generated by Claude Code

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

    @os-zhuang
    Contributor

    Triage: domain:ui (lane only — pm:on-hold untouched). Landing: packages/runner — routing of query-bearing paths and the pushState/popstate disagreement over currentPath.

    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. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    关闭 not_planned —— 维护者同意:7 周休眠,重启条件没有发生

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

    本卡说的是什么

    runner 的导航项 path 如果带 query(/customers?tab=open),会去请求 pages/customers?tab=open.json,结果 404。另外,前进时 currentPath 存的是整串,popstate 回来时只存 pathname,两个方向的口径不一致。

    本席实测(本地 main 62597c5)

    • packages/runner/src/App.tsx 的 :83 / :119 / :124 和 08-17 分诊时的行号完全一样,说明路由代码此后没人改过。
    • examples / apps / packages 里写好的导航 path 中,带 ? 的有 0 处。
    • ⇒ 5310785146 写的两个重启条件(出现带 query 的导航路径,或者有 PR 改 runner 的路由)都没有发生。

    为什么关

    7 次分诊都判为休眠。这里是开发用的 runner,没有真实触发。修法是一个设计选择(规范成 pathname、支持 query、或者在 schema 层拒绝),在没有使用场景时不值得先做。

    什么时候重开

    任一重启条件发生时重开。届时优先考虑卡面的方向 3,在契约层拒绝带 query 的导航 path。

    标签:摘 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