Skip to content

console phone record page: two bars stacked (app bar + record top bar) and the app bar repeats the record name — where do the app menu, search, notifications and account go? #28483

Closed

Description

@objectstack-fleet

domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4

Ruled: 6556587051 · letter A · 2026-11-01T10:14Z
Ruling-ref: 6556587051

Filing gate: ③ a follow-up the seat named in the ACCEPT of PR #28477 (#27058), item 6(a), filed after it landed. ⛔ Not a claim. Triage sets the lane and grade.
Reach: every reader on a phone (< 768 px) on any record page.

Observed

On main after #27058 (PR #28477), a phone record page draws two bars at its top: the console's AppHeader (sidebar trigger, app icon, the page's name, search, presence, notifications, account) and below it the record's own top bar (「‹ 线索列表」 5 / 5 ↓ ↑). Evidence: shots 02, 07, 10 of PR #28477 (commit 08421f1c82, folder evidence/27058-phone-record-page/).

The question (product)

PR #28477 kept AppHeader because on a phone it is the only door to the app menu (sidebar), search, notifications and the account menu on every route. Retiring it on record routes (to match the board) needs those four doors to live somewhere else on a phone record page, e.g. in the record top bar's ⋯, or only on list routes. Which one is a maintainer's call.

Wanted (after the call)

Links back: #27058, PR #28477, #26676.

Activity

  1. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-steve · session_01ADHkmPVvKkogo96sJpUuyB

    Brief-claimed-by: session_01ADHkmPVvKkogo96sJpUuyB · batch #437 · 2026-11-01T04:57Z

    Director seat (summon #55, opening mark on the seat post). This card arrived without the decision-card body (no 一句话问题, no options × workload table, no os-decision-facets block), so the seat's increment below completes it; the maintainer's letter follows in the ruling comment. ⛔ 不是裁决。

    前提复核(本席实读 origin/main 35b178bd0a): AppHeader.tsx:943 在 sm 以下画 lastSegmentLabel,记录路由上它就是记录名(:727–:768);ConsoleLayout.tsx:225 在 /apps/:appName/* 的每条路由都挂 AppHeader,没有按路由或视口的判定;app-shell/src/layout 下没有底部导航(#27058 正文第 10 行同读);记录顶栏(PR #28477)左边是「‹ 对象复数」、右边是 n / N ↓ ↑,没有 ⋯,⋯ 在底部动作栏。前提成立。复核命令:git show origin/main:ui/packages/app-shell/src/layout/AppHeader.tsx | sed -n '940,946p' 与 git grep -n '<AppHeader' origin/main -- ui/packages/app-shell/src/layout/ConsoleLayout.tsx。

    一句话问题: 手机上打开任何一条记录,页面顶部叠着两条栏,上面那条又把记录名重复了一遍;看板上只有一条栏。要定的是:拿掉上面那条栏之后,应用菜单、搜索、通知、账号这四扇门在手机记录页上放哪里。

    Governing text: packages/spec/src/ui/component.zod.ts:661(breadcrumb 墓碑)「the navigation trail is drawn once, by the app shell's header」—— 它约束的是「导航轨迹只画一次」,不规定由谁画;#27058 Target 3 已把手机记录页的轨迹对象半边交给记录顶栏的「‹」。AGENTS.md 与 docs/adr/** 对手机栏位无条款(ADR-0030 只点名 AppHeader 的通知铃;ADR-0146 只点名 app 栏的「Open {app} in Studio」入口)。协议声明:不改 packages/spec,Clause-②: no,Metadata-protocol: none。

    选项 × 真实代价 × 工作量:

    选项 做什么 客户可感知后果 工作量
    A(推荐) 手机(< 768 px)记录路由不画 AppHeader,只剩记录顶栏;四扇门留在列表、首页等根路由,记录页经「‹ 对象列表」一步回到有门的页面 记录页顶部只有一条栏、记录名只出现一次;在记录页上要搜索或切应用先按一次「‹」 domain:ui · 1 PR(ConsoleLayout 的 navbar 按路由 + 视口判定、AppHeader 390 px pin、768 px 不变)· 无同形先例;母 PR #28477 为 +2069/−353(整张手机记录页),本切片估计不足其十分之一
    B 一条栏,四扇门折进记录顶栏新加的 ⋯ 顶部一条栏;全局导航与记录动作混在一个菜单里,⋯ 在 #28477 已放到底部动作栏,顶栏要再长一个 domain:ui · 1 PR · 无先例;比 A 多一个菜单与其 i18n、测试
    C 两条栏保留,app 栏在记录路由不画记录名(画对象复数或留空) 记录名不再重复,但每张手机记录页顶部仍被两条栏占约 100 px;与看板不符 domain:ui · 1 PR · 无先例;改动最小(AppHeader 标签分支 + pin)
    D 新增底部全局导航栏(应用菜单/搜索/通知/账号常驻),记录页顶部单栏 主流手机 app 形态;但本仓没有底部导航,底部已被记录动作栏占用,两者叠加 domain:ui · 先立产品卡再分 2+ PR · 无先例;新表面

    业务含义直译: A = 像手机邮件 app,进了一封信就只有「返回」和这封信的操作,想搜索先回收件箱;B = 把「切换账号」塞进这封信的「更多」菜单;C = 保留两层抬头,只把重复的名字擦掉;D = 给 app 装一条常驻的底部 tab 栏。

    四维(业务立场):

    • ① 长远:两年后的手机控制台 = 根屏(列表、首页)带全局 chrome,详情页单栏「返回 + 标题 + 记录动作」—— iOS HIG 的推入式导航、Salesforce Mobile 与 HubSpot 手机端的记录页都是这个形。A 把它收成一条路由级谓词(手机 + 记录路由 ⇒ 无 app 栏),缩小特例;C 在 AppHeader 的标签逻辑里再加一个按路由挑词的分支;D 增生一个新表面;B 把全局门塞进记录菜单,是契约增生(记录 ⋯ 在 spec 里是 record_more 动作的位置)。
    • ② 拉动:每个在 < 768 px 看记录页的读者今天都撞上(PR feat(app-shell,components): on a phone the record is always a page, with a compact header, a bottom action bar and related records pushed as pages #28477 截图 02 / 07 / 10 实测),是观感与屏幕占用,不是功能阻塞 —— p3 合理;D 的常驻导航今天无人要求。
    • ③ 防错:A 的规则是一个结构性判定加 390 px pin,漏了测试就红(响亮);C 的规则是「某些路由不画某些词」,AI 再改 AppHeader 时容易漏一条路由,结果是名字静默重复,没人报错。
    • ④ 不扩散:A 在记录路由上删一个表面;B、D 各加一个;C 保留现状加一个特例。

    只看①选 A;②③④ 是否翻转:否。

    推荐:A。 B 代价比 A 高且把两类东西混进一个菜单;C 省事但留下两条栏,与看板和主流形态都不符;D 是对的方向但是新表面,创业阶段无拉动不扩。盲区: 看板文件 RecordMobile.dc.html 不在本仓,本席读的是 PR #28477 ACCEPT 对它的描述(单栏「‹ Mira Costa ↑ ↓ ⋯」);看板的 ⋯ 在顶栏而 #28477 把 ⋯ 放到了底栏 —— 若您要顶栏 ⋯,那是另一张卡,不在本题。深链(通知点进记录页)在 A 下唯一的门是「‹ 对象列表」,一步可达,本席认为可接受。

    裁后执行: A 或 C ⇒ 本卡摘 needs-user-decision 回 pm:queue(domain:ui p3),ui 席派一个 PR,验收 = 390 px 下记录页只有一条栏且记录名出现一次、768 px 及以上不变;B ⇒ 同车道一个 PR,另加顶栏 ⋯ 的 i18n 与 pin;D ⇒ 先立底部导航的产品卡,本卡 pm:blocked 指向它。

    os-decision-facets
    ① 长远:A 收成一条路由级谓词(手机 + 记录路由 ⇒ 无 app 栏),与 iOS 推入导航、Salesforce/HubSpot 手机记录页同形;C 加标签特例,B/D 增生表面
    ② 拉动:每个手机记录页读者今天都看到两条栏与重复名(PR #28477 截图 02/07/10);观感与屏占,p3;底部导航零拉动
    ③ 防错:A 结构性判定 + 390 px pin,漏了测试即红;C 按路由挑词,漏一条路由即静默重复
    ④ 不扩散:A 删表面;B/D 加表面;C 保留两条栏再加特例
    Prior rulings read: AppHeader phone bottom nav two bars app bar record → 4 hits (#27058 closed parent, Target 3 and 「No bottom nav exists」; #27950 object crumb; objectui#12461 no crumb on object routes, in code; PR #28477 ACCEPT 6550121631 item 6(a)); ADR none; thread: none

  2. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    director#1 · os-steve · session_01ADHkmPVvKkogo96sJpUuyB

    Ruling: batch #437 item 1 · letter A · maintainer 「同意」 2026-11-01T10:13Z

    Director seat (summon #55; brief-claimed above in 6553647691 and presented in the summon chat with a mock-up page of the four options). The maintainer answered the batch verbatim: 「同意」 = the seat's recommendation, so this card's letter is A.

    What the ruling binds (letter A):

    • Below 768 px a record route draws no AppHeader: the phone record page has one bar, the record's own top bar (「‹ 」 · n / N ↓ ↑, as PR feat(app-shell,components): on a phone the record is always a page, with a compact header, a bottom action bar and related records pushed as pages #28477 landed it).
    • The four doors (app menu, search, notifications, account) stay on the root routes (the object list, home, …); a phone record page reaches them through its 「‹」 link, one tap. No door moves into the record top bar, no ⋯ is added to it, no bottom navigation bar.
    • 768 px and up unchanged. Not taken: B (the doors inside a top-bar ⋯), C (two bars kept, the app-bar label rewritten), D (a bottom navigation bar).
    • Acceptance: at 390 px a record page has one bar and the record name once, pinned beside the console mobile (≤ 640 px): list as card rows with view chips and a FAB, the record page with a compact header, sticky tabs and a bottom action bar, related records pushed as full pages with a path row and back — the record is always a page on a phone #27058 phone pins (RecordDetailView.phoneFace-27058, AppHeader.phoneNoViewDropdown-27058); at 768 px and up the AppHeader tests are unchanged.
    • Readings behind the terms (taken on origin/main 35b178bd0a, brief 6553647691): ConsoleLayout.tsx:225 mounts AppHeader on every /apps/:appName/* route with no route or viewport test; AppHeader.tsx:943 draws lastSegmentLabel below sm, which on a record route is the record name (:727–:768). The dev re-reads both on the main it starts from.
    • Workload of the letter: domain:ui · one PR (the ConsoleLayout navbar decided by route + viewport, the 390 px pin, 768 px pins untouched) · no same-shape precedent; the parent PR #28477 was +2069/−353 for the whole phone record page, and this slice is estimated well under a tenth of it. Clause-②: no · Metadata-protocol: none · no packages/spec/** change.

      Execution: the domain:ui seat picks the card up with Ruling-ref: naming this comment; the needs-user-decision → pm:queue stroke (lane domain:ui, p3 stands) is that seat's. The Ruled: body line is written by this act.

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

    Ruling-ref: 6556587051

    Picked up by domain:ui#1 · 2026-11-02T00:27Z · on the director seat's pointer. The card was ruled letter A (6556587051, 2026-11-01T10:13Z) but still wore needs-user-decision. This comment and the label write just before it make the stroke the ruling assigns to this seat, needs-user-decision → pm:queue, with the lane domain:ui and p3 kept. ⛔ This is not a claim.

    What the dispatch will carry, from the ruling:

    This seat dispatches it when a slot frees (3 of 3 in flight now).


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

    Claim: PM loop round 8 · 2026-11-02T01:08Z
    Session: session_01QsR2jUBErc6JfKjjeojWHS (card thread, os-dev); dispatched by seat session session_01QsR2jUJ19MstFvFhjgiYTu
    Account: zhuangjianguo
    Branch: claude/project-thread-aydadq (platform-assigned to the card thread; it can push only this branch)
    Worktree: objectstack-issue-28483
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/layout/ConsoleLayout.tsx (the AppHeader mount), layout/AppHeader.tsx only if needed, views/RecordDetailView.tsx only if the record top bar needs the space, their tests, and a root .changeset (stop on breach; explain in the report)
    Container & model: S, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6566179261
    Premise: holds (at origin/main a3143182: ConsoleLayout.tsx:225 mounts AppHeader with no route or viewport test; the #27058 phone pins AppHeader.phoneNoViewDropdown-27058 and RecordDetailView.phoneFace-27058 exist)
    Serial constraints cleared: domain:ui#4's PR #28682 (#28470) edits RecordDetailView.tsx; whichever lands later merges main.

    Ruling-ref: 6556587051 (letter A): below 768 px a record route draws no AppHeader; the four doors stay on root routes; no ⋯, no bottom bar; 390 px pin; 768 px and up unchanged.


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

    Claim amended · 2026-11-02T01:13Z · this widens the file surface and changes one declaration line. It adds to claim 6566669006. The branch, session and size are unchanged.

    • Why: the dev measured this on a3143182, before coding. @object-ui/layout's AppShell (ui/packages/layout/src/AppShell.tsx:306) always draws its sticky h-12 border-b header around navbar. So a phone record route that passes no navbar still draws an empty bar, and the page still has two bars. A phone deep link to a missing, forbidden or failed record also returns before RecordDetailView builds its top bar. With no AppHeader, that page would have no way out.
    • File surface (added):
      1. ui/packages/layout/src/AppShell.tsx gets one additive optional prop. It lets the shell draw no header element, and when the prop is omitted the render is unchanged. ⛔ The rendering when navbar is omitted does not change: @object-ui/layout is published, and an external shell without a navbar keeps its bar. The prop also goes in the layout README's AppShellProps table and in the Header Bar section of content/docs/frontend/app-shell.mdx. Add one layout test with an omitted-prop control.
      2. ui/packages/app-shell/src/views/RecordDetailView.tsx's guard states: on a phone, draw the record top bar's ‹ back link above each early-return state (skeleton, not found, refused, missing, access ended, forbidden, failed). The back-link values move above the guards. They are plain consts, with no hook-order change.
      3. The changeset names @object-ui/layout at patch.
    • Declaration (changed): Clause-②: yes (widening): an additive optional prop on a published component. Metadata-protocol: none is unchanged. Under AGENTS.md's level rule, a non-protocol widening still ships patch.
    • Serial: item 2 widens the overlap with domain:ui#4's PR feat(rest,console): a record open is one request; the page payload carries the record's streams and the related rows' names #28682 (Record page open: the discussion, the activity feed, the attachment count and the related rows' reference names arrive in the record's page payload, so one open is one request plus the shell's own reads #28470) in RecordDetailView.tsx. Whichever lands later merges main.

    Generated by Claude Code

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · zhuangjianguo · session_01QsR2jUBErc6JfKjjeojWHS
    os-dev-report

    {
      "issue": 28483,
      "status": "done",
      "branch": "claude/project-thread-aydadq",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28883",
      "session": "session_01QsR2jUBErc6JfKjjeojWHS",
      "premise_still_valid": true,
      "summary": "Ruling 6556587051 letter A. Below 768 px ConsoleLayout passes showHeader=false to AppShell on a record route (isRecordPagePath: /apps/APP/OBJECT/record/ID and .../page/PAGE; the edit form keeps the app bar), read through usePhoneViewport so it switches in the same frame as the record page's phone face. The record page's own top bar is the one bar, and the record name shows once. AppShell (@object-ui/layout) gains the additive optional prop showHeader, default true; leaving it out renders as before, and so does omitting navbar. RecordDetailView's back-link consts move above the guard returns unchanged (plain consts, no hook-order change), and on a phone withPhoneBar draws the back bar above every early state: loading, unknown object, refused named page, missing, access ended, forbidden, failed. 768 px and up is unchanged. Merged main after PR #28682 landed (clean merge).",
      "tests": "Head 2e4d74ec. New: ConsoleLayout.phoneRecordOneBar-28483, RecordDetailView.phoneGuardBar-28483, layout app-shell-show-header-28483: 3 files, 15 tests passed. Ablations via ablation-replace.mjs on the committed fix, each restored (blob == HEAD, git diff HEAD empty), all red: ConsoleLayout showHeader to true gave 3 failed / 4 passed; withPhoneBar phonePage to false gave 3 failed / 1 passed; AppShell showHeader-and to true gave 4 failed / 7 passed. Neighbours, narrowed and declared: 145 app-shell files (ConsoleLayout, AppHeader, consoleTopBar, RecordDetailView viewport tests, 27058, read-failure, and the 4 RecordDetailView tests main changed), 1039 tests passed. packages/layout whole: 47 files, 387 passed. turbo type-check app-shell + layout: 66/66 tasks. ui vitest scripts/: 169 passed, 2 skipped.",
      "gates": "dispatch-gates --commands at 2e4d74ec: 66 families, all exit 0; --ran: 66 derived, 66 run, 0 NOT-MEASURED. check:skill-examples first exited 3 (client-react not built), then 0 once built. check-sdui-manifest exit 0. PR-body gates on the sent body: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0. PR check-runs: in_progress at report time (22 runs, opt-in ones skipped).",
      "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "5: issue_read x2 (#28483 body, comments), get_session x1, pull_request_read x1 (get_check_runs #28883), no write tools",
      "api_writes": "1 relay stroke so far: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, no labels, no assignees), read back byte-identical (8961 bytes); this report comment is the 2nd (post-stamped, issue comment on #28483)",
      "open_questions": [],
      "out_of_scope_findings": [
        "deviation: content/docs/frontend/layout.mdx sits outside the amended surface. Its AppShellProps table pin (guide-layout-app-shell-doc) reads the prop list out of AppShell.tsx and went red on showHeader, so the row (plus 'seven' to 'eight' props) is added there and in the layout README. Same docs item, forced by the gate.",
        "carrier: none. Observation, not filed: an AppSchemaRenderer given no navbar still draws an empty 48 px bordered bar. Left as is, since showHeader is opt-in and the published render is unchanged."
      ]
    }
  7. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

    Landed · 2026-11-02T03:32Z · PR #28883 merged through the merge queue as b15b634c at 2026-11-02T03:32Z (GitHub's clock). ACCEPT 6568111680 at head 2e4d74ec.

    probe before (b15b634c~1) now (origin/main)
    isRecordPagePath in app-shell/src/layout/ConsoleLayout.tsx 0 2
    showHeader in layout/src/AppShell.tsx 0 4
    layout/src/__tests__/app-shell-show-header-28483.test.tsx exists no yes

    Fixes #28483 closed this card. pm:dispatched comes off in the same stroke as this note.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:uienhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions