You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
console(record header): a url action placed at record_header on sys_user no longer renders in the 18.3 Console header menu, drawer or full page (18.2 rendered it) #29951
Ruled: 6624172200 · letter A · 2026-11-04T06:22Z · amended by 6624212682 (no 18.3.x patch; the release note is written with the release that carries A) · 2026-11-04T06:24Z
Ruling-ref: 6624172200, 6624212682
Filing gate ①: a reproduced regression in 18.3.0, split out of #29946 by triage. The #29946 report names it as a second, separate defect ("a url action at record_header on sys_user … no longer renders in the 18.3 console's header menu, in the drawer or on the full page. 18.2 rendered it."). Its evidence is in the objectstack-ai/duly 18.3 browser check (duly-18.3-browser-check/REPORT.md, R1), from duly#185. ⛔ Not a claim.
Reader:domain:ui, the record-page header action menu (ui/packages/app-shell).
What happens
Metadata: a type: 'url' action whose locations include record_header, on sys_user (the duly app's member page), is served in the metadata on 18.3.0.
18.3.0 Console: it is absent from the record header's menu, in the drawer and on the full page.
18.2.0 Console: it rendered.
Wanted
Measure first. Reproduce on main with a packaged url action at record_header on sys_user, and on a non-system object as the CONTROL. Name the commit that dropped it.
The header menu then renders a served record_headerurl action again, wherever 18.2 rendered it.
Pin: the action appears in the header menu on the drawer and on the full page. The pin is red on the dropped commit's parent behaviour.
Graded p1: a shipped regression, for a patch release.
Links: #29946 (the server half from the same report), duly#185.
Claim: PM loop round 2 · 2026-11-03T17:12Z
Session: session_01WGufzh8VRbTbBnqwnrXsSL (card thread; dispatching seat session_01WGufzjDyeFwq6AuFu3vwTd)
Account: os-support-ai
Branch: claude/project-thread-4y2o5l (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29951
Domain: domain:ui
Seat: domain:ui#1
File surface: ui/packages/app-shell/src/views/ record-header action paths (DeclaredActionsBar.tsx, RecordDetailView.tsx, recordApprovalActions.ts) and/or ui/packages/components/src/renderers/action/action-bar.tsx, their tests, one root .changeset/ file (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: xhigh
Clause-②: no
Metadata-protocol: none
Responsibility: suspected ui regression between 18.2.0 and 18.3.0 (domain:ui), unless measured to be the server half (#29946) | no platform path covers it | every Console user opening a sys_user record whose app declares a record_headerurl action (duly#185)
Thread-read: none
Premise: unmeasured at claim; the order makes the dev reproduce on origin/main6a4737ab first and stop on no-repro or a server-side cause (#29946)
Serial constraints cleared: none (#29897 is in views/metadata-admin/ResourceEditPage.tsx; #29947 and #29949 touch data-objectstack and preview/DraftChangesPanel.tsx)
Order: /mnt/project-files/ui-orders/29951.md · maintainer scope 「车队可以继续派发了,只派发 p1 p2 ,总并发不超过 20」 (2026-11-03T15:40Z)
Governing text:packages/spec/src/ui/component.zod.ts:683、:704(PageHeaderProps.actions:「An authored list is the whole set … omitting the key draws none of the object's actions」),出自 #11592 的裁决(「全部同意」选项 B)。
项目长远合理性: A 最干净:「作者列表 = 全集」保持不变,平台页用一个显式声明表达「我要对象的全部动作」,可复用于以后任何想要实时动作集的页面。B 让列表的意义取决于动作来自哪个包,还要服务端补来源戳,长期是一条隐含规则。D 把平台页的限制推给每个应用。
防 AI 写错(尤其写元数据 app): A 是显式、可校验的声明,AI 要么写 id 列表要么写开关,读得懂也查得到。B 的隐式来源规则 AI 很难推断,同一份列表在不同包里含义不同。D 最容易错:AI 在 sys_user 上加了动作、os validate 通过,界面却静默不画,正是「声明而未兑现」。
创业阶段不扩散: A 加一个键,但有真实拉动(荐长远形态一次付清);B 改面更大;D 最省但留下回归和静默丢弃。
os-decision-facets
① 项目长远合理性:A 缩小特例——「作者列表 = 全集」不变,只多一个显式开关;B 让列表含义随动作来源包而变,扩大隐含规则;D 把平台页的限制推给每个应用。
② 实际业务拉动:今天就有人撞上——duly 应用(duly#185)在 sys_user 加的 url 动作 18.2 显示、18.3 消失,属发版回归。
③ 防 AI 犯错:A 是可校验的显式声明(id 列表或开关二选一);D 让 AI 加的动作通过 os validate 却静默不画,最易犯错;B 的来源规则 AI 难以推断。
④ 创业阶段不扩散:A 新增一个永久键,但有实测拉动;D 不增键但保留静默丢弃;B 改面最大。
Prior rulings read: page:header,record_header,whole set → 0 hits; none; thread: #11592; repo: objectstack-ai/objectstack
{
"issue": 29951,
"status": "needs_decision",
"branch": "claude/project-thread-4y2o5l",
"pr": null,
"session": "session_01WGufzh8VRbTbBnqwnrXsSL",
"premise_still_valid": true,
"summary": "Reproduced on main 6a4737ab. A url action placed at record_header on sys_user by another package is served in GET /meta/object/sys_user but is drawn neither in the header row nor in its overflow menu, in the drawer or on the full page; the same action on showcase_account (CONTROL) is drawn in both. Cause is not a ui filter: 6dbff8e20a (#27219, Fixes #27053) rebuilt the platform page sys_user_detail from kind slotted (synthesized header, which reads the object's placed actions live) to kind full with a page:header naming the 11 platform action ids, and under the ruled page:header.actions contract (#11592, an authored list is the whole set) any action another package adds to sys_user is dropped. Same freeze on sys_organization_detail (4 ids) and sys_approval_request_detail (no ids). What #27219 was for, and must keep: the page owns its curated tab strip after slots.tabs retired. PM mechanism assumption falsified: no sys_ or url-type branch exists in the ui; the renderer follows the spec contract. Every fix touches that contract (a static page cannot name actions other packages add later), so this stops at a decision (os-dev, when to stop): options A to D are in the open question; the seat's analysis is comment 6611296943.",
"tests": "No code change, so no pin and no ablation. Live: full pnpm build under os-verify-lock (VERDICT command-exit 0, 119/119 tasks), showcase backend pnpm dev --fresh on its own port, Console from ui src via vite with DEV_PROXY_TARGET. A writable package com.qa.hdr stores hdr_user_profile (url, record_header, objectName sys_user) and hdr_account_profile (url, record_header, objectName showcase_account). Served: /meta/object/sys_user lists hdr_user_profile; /meta/page/sys_user_detail is kind full and its header ids are ban_user, unban_user, unlock_user, set_user_password, impersonate_user, set_user_manager, update_my_profile, change_my_password, change_my_email, resend_verification_email, delete_my_account. Playwright readings: sys_user full page row = Update Profile, Ban User, Set Password, Edit, More actions; its menu = Refresh, Impersonate User, Set Manager, Change Password, Change Email, Add to favorites, Share, Delete My Account (QA User Profile absent). sys_user drawer: same row; menu = Impersonate User, Set Manager, Change Password, Change Email, Add to favorites, Share, Open as full page, Delete My Account (absent). showcase_account full page and drawer: row = QA Account Profile, Edit, More actions (present). Unit probe (throwaway, deleted): RecordDetailView with a sys_user def holding ban_user plus a url action at record_header draws Ban User, Edit only under a kind full default page naming ban_user, and Ban User, Open Profile, Edit with no page (the synthesized header the 18.2 slotted page drew); CONTROL non-system object with no page draws Open Profile. Page history: at 6dbff8e20a parent (and at 18.2.0 27f38ada) sys_user_detail is kind slotted with no page:header; at 6dbff8e20a it is kind full with the id list.",
"gates": "NOT MEASURED: no diff, so no derived gate set and no changeset.",
"dogfood_battery": "n/a: no PR.",
"mcp_calls": "6, all reads: issue_read get 29951, issue_read get_comments 29951, issue_read get 29946, list_commits (RecordDetailView path), get_commit 27f38ada, list_commits (sys-user.page path). No MCP writes.",
"api_writes": "1: this comment via post-stamped.mjs (POST issues/29951/comments).",
"open_questions": [
{
"question": "How does a kind full platform record page draw header actions that other packages place on its object?",
"options": [
"A: additive page:header opt-in that draws every action of the bound object placed at record_header or record_more, in declaration order, read live; sys_user_detail, sys_organization_detail and sys_approval_request_detail use it instead of their frozen lists; an authored id list keeps meaning the whole set. Cost: one spec key, Metadata-protocol added, minor bump of the fixed group (not a patch); spec-key ripples, the ui reader in PageHeaderActions, three pages and their pins. Size M across spec, ui, platform-objects, plugin-approvals.",
"B: amend the contract text, no key: an authored list is the whole set of the page package's own actions, and actions another package places on the object follow it. Needs served provenance: a stored action carries _packageId (measured), a code package's folded action carries objectName only (measured on showcase_task; a cross-package code action not measured), so a server stamp too. Changes every authored page; semantic change to a ruled contract. Size M to L across server, spec, ui.",
"C (rejected): make sys_user_detail slotted again; the tabs slot is retired, so the curated strip #27219 kept would be lost.",
"D: no code; declare the 18.3 behaviour and teach that a package adding a header action to a platform object ships an app-scoped record page (PageSchema.apps, #29631) naming it. Docs, skills and a release-notes line; the regression against 18.2 stays."
],
"recommendation": "A, because it restores the 18.2 behaviour on the platform pages, keeps the strip #27219 was for and the #11592 meaning of an authored list, and is an explicit opt-in. If a patch release cannot carry a minor, D's release-notes line in 18.3.x and A in the next minor."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: this card's ruling covers the same freeze on sys_organization_detail and sys_approval_request_detail · noted, not filed"
]
}
Ruling: decision thread "Discuss pending spec rulings" · letter A, plus a 18.3.x release-notes line · maintainer 「以上同意」 2026-11-04T06:22:07Z
The maintainer was presented the seat's "待维护者裁决" above (6611296943) in the project thread with A recommended, and the attached question whether 18.3.x first ships D's release-notes line with A in the next minor (recommended yes). They answered 「以上同意」 = the recommendation on every item presented, so this card's letter is A, with the release-notes line.
Metadata-protocol: added · @objectstack/spec minor. Spec half first (domain:spec), then the ui reader and the platform-objects / approvals pages (domain:ui, the card thread on claude/project-thread-4y2o5l), with the card's pin (the action appears in the header menu on the drawer and on the full page; red on the frozen page without the opt-in).
18.3.x: A is a minor and does not go into a patch. 18.3.x ships D's release-notes line: a known limitation (the three platform record pages draw only their own listed header actions) and the interim route (an app-level record page, PageSchema.apps, fix(console): open the record page scoped to the current app (PageSchema.apps) #29631). A lands in the next minor.
Workload of the letter: M as the seat sized it: one spec key + ui read + three platform pages + approvals, each with its pin; plus one release-notes line in 18.3.x.
Execution: the ui seat that filed the question dispatches the release-notes line now and the ui half after the spec half, with Ruling-ref: naming this comment; the spec half is filed to domain:spec citing this comment. The needs-user-decision → working-state stroke is the ui seat's. The Ruled: body line is written by this act.
Ruling amendment: letter A stands; the 18.3.x release-notes line is withdrawn · maintainer 「18.3.x 不发」「等后续发版的时候再写说明」 2026-11-04T06:24Z
This amends ruling 6624172200 on one point only. The maintainer's words, verbatim: 「18.3.x 不发」 and 「等后续发版的时候再写说明」. So:
No 18.3.x patch carries anything for this card. The release-notes line that 6624172200 ordered into 18.3.x is withdrawn; nothing is dispatched for 18.3.x.
Letter A is unchanged: the spec opt-in on page:header, the three platform pages switched to it, spec half first then the ui half, Metadata-protocol: added, spec minor.
The release note is written when the release that carries A ships (the next minor), as part of that release's notes, not before.
The body's Ruled: line is updated by this act to name both comments.
Ruled 6624172200 (letter A) with the maintainer's amendment (「18.3.x 不发」「等后续发版的时候再写说明」, 2026-11-04T06:24Z): nothing ships in 18.3.x for this card, and the release note goes with the release that carries A. State: needs-user-decision → pm:blocked in this stroke. The spec half is filed as #30018 (domain:spec, p1, Ruling-ref: 6624172200). The ui half (the reader plus the three platform pages sys_user_detail, sys_organization_detail and sys_approval_request_detail, with the card's pin) stays with this seat and the card thread on claude/project-thread-4y2o5l, under Claim 6610580796. It is re-dispatched when #30018's PR merges. Assignee kept.
Claim: PM loop round 2 · 2026-11-04T12:11Z
Session: session_01WGufzh8VRbTbBnqwnrXsSL (card thread; dispatching seat session_01WGufzjDyeFwq6AuFu3vwTd)
Account: os-support-ai
Branch: claude/project-thread-4y2o5l (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29951
Domain: domain:ui
Seat: domain:ui#1
File surface: ui half of ruling A: the page:header renderer / record header action menu reading placedActions, the three platform record pages (sys_user_detail, sys_organization_detail, sys_approval_request_detail), the console registry-inputs parity test, the five non-skills "not drawn yet" qualifiers (spec describe marker + generated reference, packages/spec/liveness/page.json, content/docs/ui/pages.mdx, content/docs/protocol/objectui/layout-dsl.mdx), tests, one root .changeset/; the Tier H skills/objectstack-ui/rules/pages.md qualifier goes in a separate Refs PR (stop on breach; explain in the report)
Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: xhigh
Clause-②: no
Metadata-protocol: none
Responsibility: ui code produces it (domain:ui) | no platform path covers it | every Studio/Console user who reaches the surface named in the card body
Thread-read: 6624240449
Premise: holds: spec half #30018 landed (PR #30028, d59a1eb40d); Blocked-by: #30018 cleared. Ruling-ref: 6624172200 (A; no 18.3.x release-notes line per the maintainer amendment)
Serial constraints cleared: #30018 merged (PR #30028, 2026-11-04T12:07Z); none other in flight on the record header files (seat read 2026-11-04T12:11Z)
Order: /mnt/project-files/ui-orders/29951-ui.md · maintainer scope 「车队可以继续派发了,只派发 p1 p2 ,总并发不超过 20」 (2026-11-03T15:40Z)
{
"issue": 29951,
"status": "done",
"branch": "claude/project-thread-4y2o5l",
"pr": "https://github.com/objectstack-ai/objectstack/pull/30097",
"session": "session_01WGufzh8VRbTbBnqwnrXsSL",
"premise_still_valid": true,
"summary": "PR 1 of the ruled split (Fixes #29951, head 79a6abbc9e, draft, no assignee). PageHeaderActions (@object-ui/components) now reads page:header placedActions: true and draws every action of the page's object placed at record_header or record_more, read live, through placedHeaderActions (@object-ui/types), the one placed-header reader RecordDetailView's synthesized header now reads too; omitted or false, an authored actions list stays the whole set (#11592); a true beside a list draws none and reports once. The page:header registration declares the input. sys_user_detail, sys_organization_detail and sys_approval_request_detail set placedActions: true in place of the id lists #27219 froze; every id those lists named was placed at the header locations (the old pin held each list equal to that set), so no needed action is dropped, and the tab strips are unchanged. Five of the six qualifiers are dropped (describe marker + reference, liveness PLANNED record re-graded LIVE naming the reader, parity exemption struck, pages.mdx, layout-dsl.mdx); sdui.manifest.json, its record and content/docs/ui/components are regenerated. The sixth qualifier, skills/objectstack-ui/rules/pages.md, plus an eval the #30028 note asks for, is PR 2 (Refs #29951), not opened yet: this thread can push only claude/project-thread-4y2o5l and a branch carries one open PR, so PR 2 opens on the same branch, restarted from main, after PR 1 merges, unless a second branch is granted.",
"tests": "New pin ui/packages/app-shell/src/views/RecordDetailView.placedHeaderActions-29951.test.tsx through the real RecordDetailView, full page (route) and drawer (embedded): 7 passed. sys_user with the shipped header node draws the packaged url record_header action inline and the packaged record_more one in the overflow menu beside ban_user / impersonate_user, and draws no list_toolbar or record_section action, page and drawer; the frozen #27219 header (id list, no opt-in) draws neither packaged action, page and drawer; CONTROL qa_member: placedActions true draws exactly Ban User, Open Profile, Impersonate User, Copy Profile Link in declaration order, an authored ['ban_user'] without the key draws exactly Ban User; the refused pair draws none and logs once. Ablation: containers.tsx placedOptIn set to false && rawPlacedActions === true, 4 failed | 3 passed (Unable to find role=button name Ban User: shipped page, shipped drawer, CONTROL placed, refused pair), file restored byte-identical by cmp. ui root vitest over components page-header*, app-shell RecordDetailView.*, types __tests__: Test Files 518 passed, Tests 11620 passed; registry-inputs-spec-parity.test.ts alone 261 passed with the exemption struck. platform-objects src/pages 46 passed, plugin-approvals sys-approval-request-page 20 passed, spec src/ui/component.test.ts 361 passed. Type-check clean: @object-ui/types, components, app-shell type-check; platform-objects and plugin-approvals typecheck; spec tsc --noEmit (first app-shell run caught 2 TS7006 in the new test, fixed with MockInstance). eslint 0 errors on the 13 changed TS files. Live: pnpm build under os-verify-lock VERDICT command-exit 0, 119/119; showcase pnpm dev --fresh on 4317, Console from ui src via vite; package com.qa.hdr stores hdr_user_profile (url, record_header) on sys_user. Served pages sys_user_detail, sys_organization_detail, sys_approval_request_detail each answer kind full with page:header properties placedActions true. Playwright: Setup Users full page overflow = Refresh, Impersonate User, Set Manager, Change Password, Change Email, QA User Profile, Add to favorites, Share, Delete My Account; drawer overflow = Impersonate User, Set Manager, Change Password, Change Email, QA User Profile, Add to favorites, Share, Open as full page, Delete My Account; inline row unchanged (Update Profile, Ban User, Set Password, Edit); tab strip unchanged. Round 1 on main read the same lists without QA User Profile. CONTROL showcase_account unchanged (QA Account Profile inline).",
"gates": "node scripts/pm/dispatch-gates.mjs --commands on the final diff: 119 commands, each run with its exit captured, then --ran: 119 derived, 119 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. 118 exit 0. pnpm check:platform-checklist exit 1: ABSENT SYMBOL packages/metadata-protocol/src/protocol.ts#assertProtocolCompat in areas/platform-core.json; red the same on merge base d79eecfe5b and on origin/main f35b361997, outside this diff. First-pass reds re-run: pnpm check:sdui-reference was this diff's own (content/docs/ui/components page.mdx and index.mdx regenerated with gen-sdui-reference.mjs, committed, then exit 0); pnpm --dir ui exec vitest run scripts/ (one 15 s timeout under load) re-run alone under the lock: 170 files passed, 4985 tests passed; pnpm --filter @objectstack/spec run check:generated (check:test-typecheck tsc out of heap: this container's NODE_OPTIONS carries no heap flag, so the gate's 4096 default is skipped) re-run under the lock with --max-old-space-size=4096 appended: All 14 generated artifacts are up to date. check-changeset-no-major with --event carrying the PR body: no major, LEVEL AXIS clause-2 no, metadata-protocol none, no baseline moved. The first gate sweep ran outside os-verify-lock and alongside a locked build; every re-run and the build, type-checks and live boot were under the lock. CI check-runs on #30097 not yet read.",
"dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm.",
"mcp_calls": "This round 1, a read: pull_request_read get 30028. No MCP writes. Also 1 REST read with gh api (issues/comments/6630276117, the round 2 Claim, which names claude/project-thread-4y2o5l).",
"api_writes": "3: git push of claude/project-thread-4y2o5l; pr_create through the relay (fleet-write run 41839856588, #30097, read-back identical, assignees none); this comment via post-stamped.mjs.",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: c · reach: exception: release-text · .changeset/30018-page-header-placed-actions.md (PR #30028, unconsumed) still says the console's header renderer does not read placedActions yet and that the describe opens with the PLANNED marker; both changesets ride the next release, so its CHANGELOG entry will contradict this one · carrier: seat · noted, not filed",
"class: b · reach: named producer · pnpm check:platform-checklist is red on origin/main f35b361997: areas/platform-core.json anchors packages/metadata-protocol/src/protocol.ts#assertProtocolCompat, which the protocol.ts host split moved · carrier: seat (devx split lane) · noted, not filed"
]
}
ACCEPT 6632200052 (carried 6632303792). The card is closed by Fixes #29951 (state: closed completed). pm:dispatched is removed in this stroke.
page:headerplacedActions: true is now drawn by the console (PageHeaderActions, through the shared placedHeaderActions reader that the synthesized record header also uses). sys_user_detail, sys_organization_detail and sys_approval_request_detail opt in, so an action another package places on these objects at record_header / record_more draws again, in the drawer and on the full page.
Still owed, for the maintainer:
PR 2, Refs #29951: the skills/objectstack-ui/rules/pages.md qualifier drop (Tier H). It opens from the dev branch restarted on main, and the maintainer merges it.
queued by os-zhuang, merged by the merge queue at 2026-11-04T16:32Z, merge commit 7bafab3c6a
The objectstack-ui skill's rules/pages.md now says placedActions: true draws every placed action, read live, and evals/record-page.json has a case for it. Both halves of #29951 have now landed. One item is left for release time: #30018's pending changeset sentence (see the ui-half landing record above).
Activity
objectstack-fleet commentedon Nov 3, 2026
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd
Claim: PM loop round 2 · 2026-11-03T17:12Z
Session:
session_01WGufzh8VRbTbBnqwnrXsSL(card thread; dispatching seatsession_01WGufzjDyeFwq6AuFu3vwTd)Account:
os-support-aiBranch:
claude/project-thread-4y2o5l(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29951Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/app-shell/src/views/record-header action paths (DeclaredActionsBar.tsx,RecordDetailView.tsx,recordApprovalActions.ts) and/orui/packages/components/src/renderers/action/action-bar.tsx, their tests, one root.changeset/file (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: suspected ui regression between 18.2.0 and 18.3.0 (
domain:ui), unless measured to be the server half (#29946) | no platform path covers it | every Console user opening asys_userrecord whose app declares arecord_headerurlaction (duly#185)Thread-read: none
Premise: unmeasured at claim; the order makes the dev reproduce on
origin/main6a4737abfirst and stop on no-repro or a server-side cause (#29946)Serial constraints cleared: none (#29897 is in
views/metadata-admin/ResourceEditPage.tsx; #29947 and #29949 touchdata-objectstackandpreview/DraftChangesPanel.tsx)Order:
/mnt/project-files/ui-orders/29951.md· maintainer scope 「车队可以继续派发了,只派发 p1 p2 ,总并发不超过 20」 (2026-11-03T15:40Z)objectstack-fleet commentedon Nov 3, 2026
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd
待维护者裁决 · #29951 · 2026-11-03T17:45Z
一句话: 18.3 里
sys_user记录页不再显示其他包加到记录头的动作,不是前端过滤造成的,而是 #27219 把平台的sys_user_detail改成了kind: 'full'页面并写死了 11 个动作 id;按现行契约「作者列出的列表就是全集」,别的包后加的动作一律不画。修它就要动这条契约,所以停下来请你裁。Governing text:
packages/spec/src/ui/component.zod.ts:683、:704(PageHeaderProps.actions:「An authored list is the whole set … omitting the key draws none of the object's actions」),出自 #11592 的裁决(「全部同意」选项 B)。实测(dev 读数全文:
/mnt/project-files/ui-orders/29951-findings.md;dev 报告评论随后贴本卡):6a4737ab实机启动:sys_user上一个record_header的url动作确实由服务端下发(GET /api/v1/meta/object/sys_user里有),但整页和抽屉的头部与 ⋯ 菜单都没有;同样的动作放在showcase_account上(对照)两处都显示。6dbff8e20a(feat(spec,ui)!: retire the record page's slots.tabs; a page that needs its own strip is kind: 'full' #27219,2026-10-28)。之前sys_user_detail是slotted,头部由合成器实时读对象的全部记录头动作;之后是full+ 固定 id 列表。sys_organization_detail、sys_approval_request_detail同样被冻结。slots.tabs已退役),这一点任何方案都不能回退。选项
page:header上加一个显式开关:「画出绑定对象所有放在record_header/record_more的动作,实时读取」;三个平台页改用它。作者写的 id 列表仍然是全集,#11592 的含义不变。Metadata-protocol: added,minor)+ ui 读取 + platform-objects 三页 + approvals,各自的钉子sys_user_detail改回slotted。slots.tabs已退役,不可行PageSchema.apps,#29631)。四维分析
sys_user上加了 url 动作,18.2 显示、18.3 消失,真实部署在用。A、B 恢复它;D 让每个这样的应用多写一个页面才行。sys_user上加了动作、os validate通过,界面却静默不画,正是「声明而未兑现」。os-decision-facets
① 项目长远合理性:A 缩小特例——「作者列表 = 全集」不变,只多一个显式开关;B 让列表含义随动作来源包而变,扩大隐含规则;D 把平台页的限制推给每个应用。
② 实际业务拉动:今天就有人撞上——duly 应用(duly#185)在
sys_user加的 url 动作 18.2 显示、18.3 消失,属发版回归。③ 防 AI 犯错:A 是可校验的显式声明(id 列表或开关二选一);D 让 AI 加的动作通过
os validate却静默不画,最易犯错;B 的来源规则 AI 难以推断。④ 创业阶段不扩散:A 新增一个永久键,但有实测拉动;D 不增键但保留静默丢弃;B 改面最大。
Prior rulings read: page:header,record_header,whole set → 0 hits; none; thread: #11592; repo: objectstack-ai/objectstack
席位推荐:A。 若 18.3.x 补丁不能带 minor:18.3.x 先发 D 的发布说明一行(明说已知限制和临时做法),A 进下一个 minor。A 涉及新 spec 键,裁定后按契约先行由 spec 车道起 spec 半边,本卡的 dev 线程(分支
claude/project-thread-4y2o5l)等裁决,可接 ui 与平台页半边。需要你做的: 在 A / B / D 中选一个(以及 18.3.x 是否先发 D 的说明)。
objectstack-fleet commentedon Nov 3, 2026
domain:ui#1 · os-support-ai · session_01WGufzh8VRbTbBnqwnrXsSL
os-dev-report
Stamp: 2026-11-03T17:48Z
{ "issue": 29951, "status": "needs_decision", "branch": "claude/project-thread-4y2o5l", "pr": null, "session": "session_01WGufzh8VRbTbBnqwnrXsSL", "premise_still_valid": true, "summary": "Reproduced on main 6a4737ab. A url action placed at record_header on sys_user by another package is served in GET /meta/object/sys_user but is drawn neither in the header row nor in its overflow menu, in the drawer or on the full page; the same action on showcase_account (CONTROL) is drawn in both. Cause is not a ui filter: 6dbff8e20a (#27219, Fixes #27053) rebuilt the platform page sys_user_detail from kind slotted (synthesized header, which reads the object's placed actions live) to kind full with a page:header naming the 11 platform action ids, and under the ruled page:header.actions contract (#11592, an authored list is the whole set) any action another package adds to sys_user is dropped. Same freeze on sys_organization_detail (4 ids) and sys_approval_request_detail (no ids). What #27219 was for, and must keep: the page owns its curated tab strip after slots.tabs retired. PM mechanism assumption falsified: no sys_ or url-type branch exists in the ui; the renderer follows the spec contract. Every fix touches that contract (a static page cannot name actions other packages add later), so this stops at a decision (os-dev, when to stop): options A to D are in the open question; the seat's analysis is comment 6611296943.", "tests": "No code change, so no pin and no ablation. Live: full pnpm build under os-verify-lock (VERDICT command-exit 0, 119/119 tasks), showcase backend pnpm dev --fresh on its own port, Console from ui src via vite with DEV_PROXY_TARGET. A writable package com.qa.hdr stores hdr_user_profile (url, record_header, objectName sys_user) and hdr_account_profile (url, record_header, objectName showcase_account). Served: /meta/object/sys_user lists hdr_user_profile; /meta/page/sys_user_detail is kind full and its header ids are ban_user, unban_user, unlock_user, set_user_password, impersonate_user, set_user_manager, update_my_profile, change_my_password, change_my_email, resend_verification_email, delete_my_account. Playwright readings: sys_user full page row = Update Profile, Ban User, Set Password, Edit, More actions; its menu = Refresh, Impersonate User, Set Manager, Change Password, Change Email, Add to favorites, Share, Delete My Account (QA User Profile absent). sys_user drawer: same row; menu = Impersonate User, Set Manager, Change Password, Change Email, Add to favorites, Share, Open as full page, Delete My Account (absent). showcase_account full page and drawer: row = QA Account Profile, Edit, More actions (present). Unit probe (throwaway, deleted): RecordDetailView with a sys_user def holding ban_user plus a url action at record_header draws Ban User, Edit only under a kind full default page naming ban_user, and Ban User, Open Profile, Edit with no page (the synthesized header the 18.2 slotted page drew); CONTROL non-system object with no page draws Open Profile. Page history: at 6dbff8e20a parent (and at 18.2.0 27f38ada) sys_user_detail is kind slotted with no page:header; at 6dbff8e20a it is kind full with the id list.", "gates": "NOT MEASURED: no diff, so no derived gate set and no changeset.", "dogfood_battery": "n/a: no PR.", "mcp_calls": "6, all reads: issue_read get 29951, issue_read get_comments 29951, issue_read get 29946, list_commits (RecordDetailView path), get_commit 27f38ada, list_commits (sys-user.page path). No MCP writes.", "api_writes": "1: this comment via post-stamped.mjs (POST issues/29951/comments).", "open_questions": [ { "question": "How does a kind full platform record page draw header actions that other packages place on its object?", "options": [ "A: additive page:header opt-in that draws every action of the bound object placed at record_header or record_more, in declaration order, read live; sys_user_detail, sys_organization_detail and sys_approval_request_detail use it instead of their frozen lists; an authored id list keeps meaning the whole set. Cost: one spec key, Metadata-protocol added, minor bump of the fixed group (not a patch); spec-key ripples, the ui reader in PageHeaderActions, three pages and their pins. Size M across spec, ui, platform-objects, plugin-approvals.", "B: amend the contract text, no key: an authored list is the whole set of the page package's own actions, and actions another package places on the object follow it. Needs served provenance: a stored action carries _packageId (measured), a code package's folded action carries objectName only (measured on showcase_task; a cross-package code action not measured), so a server stamp too. Changes every authored page; semantic change to a ruled contract. Size M to L across server, spec, ui.", "C (rejected): make sys_user_detail slotted again; the tabs slot is retired, so the curated strip #27219 kept would be lost.", "D: no code; declare the 18.3 behaviour and teach that a package adding a header action to a platform object ships an app-scoped record page (PageSchema.apps, #29631) naming it. Docs, skills and a release-notes line; the regression against 18.2 stays." ], "recommendation": "A, because it restores the 18.2 behaviour on the platform pages, keeps the strip #27219 was for and the #11592 meaning of an authored list, and is an explicit opt-in. If a patch release cannot carry a minor, D's release-notes line in 18.3.x and A in the next minor." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: this card's ruling covers the same freeze on sys_organization_detail and sys_approval_request_detail · noted, not filed" ] }Generated by Claude Code
os-support-ai commentedon Nov 4, 2026
decision thread · os-support-ai · session_01WGufzepGFmf3XtoZaZuheq
Ruling: decision thread "Discuss pending spec rulings" · letter A, plus a 18.3.x release-notes line · maintainer 「以上同意」 2026-11-04T06:22:07Z
The maintainer was presented the seat's "待维护者裁决" above (6611296943) in the project thread with A recommended, and the attached question whether 18.3.x first ships D's release-notes line with A in the next minor (recommended yes). They answered 「以上同意」 = the recommendation on every item presented, so this card's letter is A, with the release-notes line.
What the ruling binds (letter A):
@objectstack/specadds one explicit opt-in onpage:header: draw every action of the bound object placed atrecord_header/record_more, read live, in place of an authored id list. The authoredactionslist keeps itsPageHeaderProps.actionsdeclaresz.array(z.string())but objectui's canonicalpage:headerrenderer consumes ActionDef objects — satisfying the schema deletes every header button #11592 meaning (an authored list is the whole set); the new key is the other, explicit way to say "all placed actions". Spelling and exclusivity withactionsare the spec seat's call, named in the PR for the maintainer.sys_user_detail,sys_organization_detail,sys_approval_request_detail) switch to the opt-in, so an action another package places onsys_useris drawn again in the header menu, the drawer and the full page; feat(spec,ui)!: retire the record page's slots.tabs; a page that needs its own strip is kind: 'full' #27219's tab bar is kept.Metadata-protocol: added·@objectstack/specminor. Spec half first (domain:spec), then the ui reader and the platform-objects / approvals pages (domain:ui, the card thread onclaude/project-thread-4y2o5l), with the card's pin (the action appears in the header menu on the drawer and on the full page; red on the frozen page without the opt-in).PageSchema.apps, fix(console): open the record page scoped to the current app (PageSchema.apps) #29631). A lands in the next minor.slotted, undoes feat(spec,ui)!: retire the record page's slots.tabs; a page that needs its own strip is kind: 'full' #27219), D alone (regression kept).Workload of the letter: M as the seat sized it: one spec key + ui read + three platform pages + approvals, each with its pin; plus one release-notes line in 18.3.x.
Execution: the ui seat that filed the question dispatches the release-notes line now and the ui half after the spec half, with
Ruling-ref:naming this comment; the spec half is filed todomain:specciting this comment. Theneeds-user-decision→ working-state stroke is the ui seat's. TheRuled:body line is written by this act.Generated by Claude Code
os-support-ai commentedon Nov 4, 2026
decision thread · os-support-ai · session_01WGufzepGFmf3XtoZaZuheq
Ruling amendment: letter A stands; the 18.3.x release-notes line is withdrawn · maintainer 「18.3.x 不发」「等后续发版的时候再写说明」 2026-11-04T06:24Z
This amends ruling 6624172200 on one point only. The maintainer's words, verbatim: 「18.3.x 不发」 and 「等后续发版的时候再写说明」. So:
page:header, the three platform pages switched to it, spec half first then the ui half,Metadata-protocol: added, spec minor.The body's
Ruled:line is updated by this act to name both comments.Generated by Claude Code
objectstack-fleet commentedon Nov 4, 2026
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd
Blocked-by: #30018
Ruled 6624172200 (letter A) with the maintainer's amendment (「18.3.x 不发」「等后续发版的时候再写说明」, 2026-11-04T06:24Z): nothing ships in 18.3.x for this card, and the release note goes with the release that carries A. State:
needs-user-decision→pm:blockedin this stroke. The spec half is filed as #30018 (domain:spec, p1,Ruling-ref: 6624172200). The ui half (the reader plus the three platform pagessys_user_detail,sys_organization_detailandsys_approval_request_detail, with the card's pin) stays with this seat and the card thread onclaude/project-thread-4y2o5l, under Claim 6610580796. It is re-dispatched when #30018's PR merges. Assignee kept.objectstack-fleet commentedon Nov 4, 2026
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd
Claim: PM loop round 2 · 2026-11-04T12:11Z
Session:
session_01WGufzh8VRbTbBnqwnrXsSL(card thread; dispatching seatsession_01WGufzjDyeFwq6AuFu3vwTd)Account:
os-support-aiBranch:
claude/project-thread-4y2o5l(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29951Domain:
domain:uiSeat:
domain:ui#1File surface: ui half of ruling A: the
page:headerrenderer / record header action menu readingplacedActions, the three platform record pages (sys_user_detail,sys_organization_detail,sys_approval_request_detail), the console registry-inputs parity test, the five non-skills "not drawn yet" qualifiers (spec describe marker + generated reference,packages/spec/liveness/page.json,content/docs/ui/pages.mdx,content/docs/protocol/objectui/layout-dsl.mdx), tests, one root.changeset/; the Tier Hskills/objectstack-ui/rules/pages.mdqualifier goes in a separateRefsPR (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT(dispatch-gates --tier: no path-derived mandate),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: ui code produces it (
domain:ui) | no platform path covers it | every Studio/Console user who reaches the surface named in the card bodyThread-read: 6624240449
Premise: holds: spec half #30018 landed (PR #30028,
d59a1eb40d);Blocked-by: #30018cleared.Ruling-ref: 6624172200(A; no 18.3.x release-notes line per the maintainer amendment)Serial constraints cleared: #30018 merged (PR #30028, 2026-11-04T12:07Z); none other in flight on the record header files (seat read 2026-11-04T12:11Z)
Order:
/mnt/project-files/ui-orders/29951-ui.md· maintainer scope 「车队可以继续派发了,只派发 p1 p2 ,总并发不超过 20」 (2026-11-03T15:40Z)objectstack-fleet commentedon Nov 4, 2026
domain:ui#1 · os-support-ai · session_01WGufzh8VRbTbBnqwnrXsSL
os-dev-report
Stamp: 2026-11-04T13:39Z
{ "issue": 29951, "status": "done", "branch": "claude/project-thread-4y2o5l", "pr": "https://github.com/objectstack-ai/objectstack/pull/30097", "session": "session_01WGufzh8VRbTbBnqwnrXsSL", "premise_still_valid": true, "summary": "PR 1 of the ruled split (Fixes #29951, head 79a6abbc9e, draft, no assignee). PageHeaderActions (@object-ui/components) now reads page:header placedActions: true and draws every action of the page's object placed at record_header or record_more, read live, through placedHeaderActions (@object-ui/types), the one placed-header reader RecordDetailView's synthesized header now reads too; omitted or false, an authored actions list stays the whole set (#11592); a true beside a list draws none and reports once. The page:header registration declares the input. sys_user_detail, sys_organization_detail and sys_approval_request_detail set placedActions: true in place of the id lists #27219 froze; every id those lists named was placed at the header locations (the old pin held each list equal to that set), so no needed action is dropped, and the tab strips are unchanged. Five of the six qualifiers are dropped (describe marker + reference, liveness PLANNED record re-graded LIVE naming the reader, parity exemption struck, pages.mdx, layout-dsl.mdx); sdui.manifest.json, its record and content/docs/ui/components are regenerated. The sixth qualifier, skills/objectstack-ui/rules/pages.md, plus an eval the #30028 note asks for, is PR 2 (Refs #29951), not opened yet: this thread can push only claude/project-thread-4y2o5l and a branch carries one open PR, so PR 2 opens on the same branch, restarted from main, after PR 1 merges, unless a second branch is granted.", "tests": "New pin ui/packages/app-shell/src/views/RecordDetailView.placedHeaderActions-29951.test.tsx through the real RecordDetailView, full page (route) and drawer (embedded): 7 passed. sys_user with the shipped header node draws the packaged url record_header action inline and the packaged record_more one in the overflow menu beside ban_user / impersonate_user, and draws no list_toolbar or record_section action, page and drawer; the frozen #27219 header (id list, no opt-in) draws neither packaged action, page and drawer; CONTROL qa_member: placedActions true draws exactly Ban User, Open Profile, Impersonate User, Copy Profile Link in declaration order, an authored ['ban_user'] without the key draws exactly Ban User; the refused pair draws none and logs once. Ablation: containers.tsx placedOptIn set to false && rawPlacedActions === true, 4 failed | 3 passed (Unable to find role=button name Ban User: shipped page, shipped drawer, CONTROL placed, refused pair), file restored byte-identical by cmp. ui root vitest over components page-header*, app-shell RecordDetailView.*, types __tests__: Test Files 518 passed, Tests 11620 passed; registry-inputs-spec-parity.test.ts alone 261 passed with the exemption struck. platform-objects src/pages 46 passed, plugin-approvals sys-approval-request-page 20 passed, spec src/ui/component.test.ts 361 passed. Type-check clean: @object-ui/types, components, app-shell type-check; platform-objects and plugin-approvals typecheck; spec tsc --noEmit (first app-shell run caught 2 TS7006 in the new test, fixed with MockInstance). eslint 0 errors on the 13 changed TS files. Live: pnpm build under os-verify-lock VERDICT command-exit 0, 119/119; showcase pnpm dev --fresh on 4317, Console from ui src via vite; package com.qa.hdr stores hdr_user_profile (url, record_header) on sys_user. Served pages sys_user_detail, sys_organization_detail, sys_approval_request_detail each answer kind full with page:header properties placedActions true. Playwright: Setup Users full page overflow = Refresh, Impersonate User, Set Manager, Change Password, Change Email, QA User Profile, Add to favorites, Share, Delete My Account; drawer overflow = Impersonate User, Set Manager, Change Password, Change Email, QA User Profile, Add to favorites, Share, Open as full page, Delete My Account; inline row unchanged (Update Profile, Ban User, Set Password, Edit); tab strip unchanged. Round 1 on main read the same lists without QA User Profile. CONTROL showcase_account unchanged (QA Account Profile inline).", "gates": "node scripts/pm/dispatch-gates.mjs --commands on the final diff: 119 commands, each run with its exit captured, then --ran: 119 derived, 119 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. 118 exit 0. pnpm check:platform-checklist exit 1: ABSENT SYMBOL packages/metadata-protocol/src/protocol.ts#assertProtocolCompat in areas/platform-core.json; red the same on merge base d79eecfe5b and on origin/main f35b361997, outside this diff. First-pass reds re-run: pnpm check:sdui-reference was this diff's own (content/docs/ui/components page.mdx and index.mdx regenerated with gen-sdui-reference.mjs, committed, then exit 0); pnpm --dir ui exec vitest run scripts/ (one 15 s timeout under load) re-run alone under the lock: 170 files passed, 4985 tests passed; pnpm --filter @objectstack/spec run check:generated (check:test-typecheck tsc out of heap: this container's NODE_OPTIONS carries no heap flag, so the gate's 4096 default is skipped) re-run under the lock with --max-old-space-size=4096 appended: All 14 generated artifacts are up to date. check-changeset-no-major with --event carrying the PR body: no major, LEVEL AXIS clause-2 no, metadata-protocol none, no baseline moved. The first gate sweep ran outside os-verify-lock and alongside a locked build; every re-run and the build, type-checks and live boot were under the lock. CI check-runs on #30097 not yet read.", "dogfood_battery": "n/a: no ! in the PR title and no (narrowing) arm.", "mcp_calls": "This round 1, a read: pull_request_read get 30028. No MCP writes. Also 1 REST read with gh api (issues/comments/6630276117, the round 2 Claim, which names claude/project-thread-4y2o5l).", "api_writes": "3: git push of claude/project-thread-4y2o5l; pr_create through the relay (fleet-write run 41839856588, #30097, read-back identical, assignees none); this comment via post-stamped.mjs.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: c · reach: exception: release-text · .changeset/30018-page-header-placed-actions.md (PR #30028, unconsumed) still says the console's header renderer does not read placedActions yet and that the describe opens with the PLANNED marker; both changesets ride the next release, so its CHANGELOG entry will contradict this one · carrier: seat · noted, not filed", "class: b · reach: named producer · pnpm check:platform-checklist is red on origin/main f35b361997: areas/platform-core.json anchors packages/metadata-protocol/src/protocol.ts#assertProtocolCompat, which the protocol.ts host split moved · carrier: seat (devx split lane) · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 4, 2026
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd
Landed · ui half · PR #30097 ·
a52761fdd7· 2026-11-04T14:46Za52761fdd76d81dac2, CI green (58 success, 7 expected skips)d59a1eb40d), landed earlierACCEPT 6632200052 (carried 6632303792). The card is closed by
Fixes #29951(state: closed completed).pm:dispatchedis removed in this stroke.page:headerplacedActions: trueis now drawn by the console (PageHeaderActions, through the sharedplacedHeaderActionsreader that the synthesized record header also uses).sys_user_detail,sys_organization_detailandsys_approval_request_detailopt in, so an action another package places on these objects atrecord_header/record_moredraws again, in the drawer and on the full page.Refs #29951: theskills/objectstack-ui/rules/pages.mdqualifier drop (Tier H). It opens from the dev branch restarted onmain, and the maintainer merges it.minorchangeset and every gate stayed green #17712), so that sentence is corrected in the release notes the maintainer writes («等后续发版的时候再写说明»).objectstack-fleet commentedon Nov 4, 2026
domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd
Landed · skills half · PR #30127 ·
7bafab3c6a· 2026-11-04T16:32ZRefs #29951, Tier H)7bafab3c6aThe
objectstack-uiskill'srules/pages.mdnow saysplacedActions: truedraws every placed action, read live, andevals/record-page.jsonhas a case for it. Both halves of #29951 have now landed. One item is left for release time: #30018's pending changeset sentence (see the ui-half landing record above).