Repository navigation
Description
Ruled: 6249771678 · letter E1 · 2026-10-18T12:12Z
Filed by the director seat (summon #43 continuation 5, session_01QJC7i981TSs9L1CGJK59Ax) on the maintainer's question in this session's chat, verbatim: 「还有很多业务对象都有类似的问题,就是 action很多,这种有好的通用方案吗」 (2026-10-18, right after confirming cloud#3001's design). ⛔ Not a claim. A needs-user-decision card: the platform half of cloud#3001, under the maintainer's standing 口径 「平台相关的功能,应该尽量先改平台。」 (batch #362, #24256).
Dedupe: open objectstack / objectui cards on record_section, settings tab, danger zone, record_header: none; cloud#3001 (ruled A — the environment page authored from existing blocks) is the first consumer-side instance; check-prior-rulings on these terms: 0 ADR hits.
维护者速读
您问的是:动作很多的对象,有没有通用方案。平台今天的做法是页头最多放 3 个按钮,其余全部折进 ⋯ 菜单。spec 里早就有 record_section(「记录正文分区/页签里的动作」,文档举的例子就是「安全页签放改密码、两步验证」),也有 record:quick_actions 块来摆它们,但默认记录页不画它们 —— 想要「设置」页签,今天每个对象都得自己写一张页面(平台的 sys_user 安全页签、cloud 的环境页都是这么做的)。通用方案就是把这件事做进平台:对象把管理类动作声明到 record_section 并标一个分组,默认记录页自动长出「设置」页签,按分组分区,danger 动作自动落到底部红框;再加一条 lint,页头动作超过 5 个就警告。按源码数,今天真正超出页头的只有两个对象(环境 24 个、用户 10 个),hotcrm 的对象最多 3 个,所以这是提前把路修好,不是救火。需要您定的一点:这件事的渲染部分必然是 objectui 里的 React 代码 —— 它是给所有对象用的渲染器能力,不是某一个页面;如果您对 3001 说的「不在 objectui 用 React」也适用于这里,那就只能选 B。
What the platform does today (read at objectstack 3fed1eb00af6, objectui a8560b228527, cloud 837e24a4b8b4, hotcrm e50d048b9146)
- Header fold. objectui
packages/components/src/renderers/layout/containers.tsx:2008–2066: up tomaxVisible(default 3, mobile 1; apage:headerprop)record_headeractions render as buttons; the rest, and everyrecord_more-only action, collapse into the ⋯ menu in three bands (objectui#12529). specaction.zod.ts:1314–1327:orderpromotes an action into the primary slot or demotes it toward ⋯. The console also injects 分享, 收藏 and 删除 into every record's ⋯ (RecordDetailView.tsx:3059–3187), so a walk of the product shows a fuller menu than the object's own declarations. - The vocabulary exists, the default rendering does not. spec
action.zod.ts:647:record_section= 「actions surfaced inside a body section/tab of a record (e.g. a Security tab grouping change-password, 2FA, etc.)」;record:quick_actions(component.zod.ts) places them byactionNamesandlocation. The synthesized default record page drawsrecord_sectionactions only in the approvals decision bar (RecordDetailView.tsx:3577). An object gets a settings tab only by authoring a page:packages/platform-objects/src/pages/sys-user.page.ts:359–495(a 「安全」 tab of fourrecord:quick_actionsgroups) and, from today's ruling, cloud's environment page (cloud#3001 A). - No grouping key. An action declares
locations,order,variant(primary|secondary|danger|ghost|link) anddescription; nothing names the section an action belongs to.ActionDefinitionrefuses unknown keys. - How many objects are over the fold, by declared metadata.
record_headerper object: cloudsys_environment24 (cloud#3001); objectstacksys_user10 (+3record_section-only; already authored),sys_oauth_application4,sys_sso_provider3,sys_organization3; cloudsys_package_version4,sys_domain3,sys_cloud_connection3; hotcrmcase3,campaign3, the rest ≤2 (src/*/actions/*.actions.ts). Two objects exceed the three-button fold by a wide margin; three lose one action into ⋯; everything else fits.
Options
| Option | What is built | Workload |
|---|---|---|
| A · Platform default 「设置」 tab (seat's recommendation) | spec: one optional key on ActionDefinition, group (an i18n label — the heading of the section the action sits under); os check lint: more than 5 record_header actions on one object → warning naming record_more / record_section. objectui: the synthesized default record page adds a 「设置」 tab when the object declares ≥1 record_section action — sections by group (ungrouped under 「常规」), one row per action (label, description as the line of context, the button), variant: 'danger' actions in a red-bordered 「危险操作」 zone at the bottom whatever their group; the same visible / params / confirm path as the header. Authored pages are untouched. Consumers: move management actions from record_header to record_section, set group; no page authoring. |
spec S · objectui M · each consumer S; two pins before cloud / hotcrm use it |
| B · Guard only | the os check lint above, plus the existing levers (record_more, order, maxVisible); a settings tab stays an authored page per object (sys_user, the environment page) |
spec S; no generic page |
| C · Convention only | no platform change; the authoring docs name the pattern (a page:tabs 「设置」 of record:quick_actions groups) and every object copies it |
docs |
Recommendation: A
- Long-term fit. The spec already names
record_sectionwith exactly this example; what the platform lacks is the default rendering, and the 口径 says build it in the platform. One new key, no new location, no new block. B and C leave every application re-authoring the same tab. - Business pull. Real on cloud (24 actions met on staging), authored by hand twice already (
sys_user, the environment page); by declared metadata no third object is over the fold yet, so A is road-building ahead of the next object, not a fire. The maintainer's walk reports more objects than the metadata shows; the injected 分享 / 收藏 / 删除 account for part of that. - AI-error surface.
groupis strict-validated as a key; a misspelled group value makes a visible extra heading, not a silent drop. The lint catches the habit of piling new actions intorecord_header. A default tab means no hand-written page per object to get wrong. - Startup, no spread. A touches spec and objectui once; consumers move
locationsonly; no governed file. B is one lint; C builds nothing. - The one thing to confirm. A's rendering half is React in objectui — a renderer capability every object gets from metadata, which is the platform's job, unlike a one-object page (what 「不应该在objectui 中用react做」 refused on cloud#3001). If that sentence applies here too, the answer is B.
- Nothing waits on A. Any object can have the tab today by authoring a page as
sys_userand the environment page do.
Execution
- A: the director files, after the letter: an objectstack spec card p2
pm:queue(groupkey, docs, the lint, withsys_user's own actions as the first declarer); an objectui default-page card p2pm:blockedon the spec pin; no cloud or hotcrm card until an object needs the tab (the environment page stays authored per cloud#3001 A). - B: one objectstack spec card p2 (the lint).
- C: one docs line, no card.
os-decision-facets
① 项目长远合理性:A —— spec 早已声明 record_section,缺的只是默认渲染;按口径进平台;只加一个 group 键,不加位置、不加块;B/C 让每个应用重复写同一张页签。
② 实际业务拉动:cloud 真实(staging 撞上 24 个);已手写两次(sys_user、环境页);按元数据今天没有第三个对象超出页头,A 是提前修路;您走查看到的更多,部分来自控制台注入的分享/收藏/删除。
③ 防 AI 犯错:group 键严格校验,值写错是多出一个可见标题而非静默丢失;lint 拦住往页头堆动作的习惯;默认页签免去每对象手写页面。
④ 创业阶段不扩散:A 只改 spec 与 objectui 各一次,消费方只挪 locations;不碰受管文件;B 一条 lint;C 不建。
Prior rulings read: cloud#3001 ruling A(环境页用现有块拼「设置」页签)· cloud#2774 第一阶段 6122033877 · 总监席 batch #362 口径「平台相关的功能,应该尽量先改平台。」(#24256)· spec record_section / record:quick_actions 文档 · objectui#2361 / #12529(页头折叠)→ 4 hits; ADR none; thread: 0
Activity
objectstack-fleet commentedon Oct 18, 2026
席位意见(增量 · 效果图与 cloud#3001 的关系)· director seat (summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax) · on batch #365 item 1, 2026-10-18T10:05ZThe maintainer's words in this session's chat, verbatim: 「24277 如果按照这个开发,开发出的效果图会是什么样?cloud 3001 还需要吗?」 and 「3001 为什么关闭,还需要开发吗」. Two answers, no letter yet.
1. What option A renders, drawn. Two artboards added to the cloud#3001 canvas, https://claude.ai/artifact/WEQfNGTRQw19uqBVH3eqKb (second row): 「环境 · 默认记录页」 and 「用户 (
sys_user) · 默认记录页」. Both are the console's default record page with zero page code: the header is the record chrome (⟳, the object'srecord_headeractions up tomaxVisible, ⋯), the tab strip is the synthesizer's 详情 / 相关 / 活动 plus the generated 「设置」; inside it one section pergroupin declaration order, one row perrecord_sectionaction (the action'slabel, itsdescriptionas the muted line, its button), and a red-bordered 「危险操作」 section at the bottom that collects everyvariant: 'danger'action whatever its group. The rendered PNGs went to the maintainer as files (the relay cannot upload images).2. Why cloud#3001 is closed, and whether it still needs development. cloud#3001 was a decision card, not an execution card: its own 「执行 · 取 A」 line says the card closes once the letter is recorded and points at cloud#2916, the execution card. The ruling (6247273688) bound the 「设置」 tab and the confirmed design onto cloud#2916 (pointer 6247289049), which is
pm:blockedon the objectui pin carrying objectstack-ai/objectui#12933. So yes, the development is still owed — in cloud#2916's round, by therepo:cloud#2seat, as ruled.record_sectionactions (specgroupkey + objectui default record page) and arecord_headercount lint #24277 is ruled A: cloud#2916 stops authoring the fiverecord:quick_actionsblocks; cloud only moves the actions'locationstorecord_section, setsgroup, and the tab appears by itself after the objectui pin that carries A (the same kind of pin wait cloud#2916 already has). The 概览 half of cloud#2916 Target 2 (header with 打开 / 安装应用 / 升级 on Free, the summary row, the installed-apps card, the URL card, the read-only 访问与登录) stays authored — the default page does not draw it. The confirmed design stays the 样式 for both: what cloud authors, and what the platform's default tab should look like. The director records this on cloud#2916 with the letter.record_sectionactions (specgroupkey + objectui default record page) and arecord_headercount lint #24277 is ruled B or C: cloud#2916 builds the 「设置」 tab exactly as cloud#3001 ruled A; nothing changes.os-decision-facets
locations加group)。自检:只看①选 A;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 18, 2026
席位意见(增量 2 · 页签何时出现;一处更正)· director seat (summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax) · on batch #365 item 1, 2026-10-18T10:07ZThe maintainer's words in this session's chat, verbatim: 「对象在场景下会生成这样的设置页签呢」 — read as: under what conditions does an object get the generated 「设置」 tab. The rules option A proposes, stated so a dev can pin them in tests:
When the tab is generated (default record page only).
locationscontainsrecord_section. Nothing else: no object-level switch, no page metadata. An object with norecord_sectionaction gets no tab and sees no change — today that is every hotcrm object and every cloud object exceptsys_environmentafter its actions move.visibleCEL and permission gate pass for this viewer on this record (the same path the header uses). When nothing passes, the tab is not drawn for that viewer.sys_user's actions arerecord.id == ctx.user.id, so its tab would show on your own record only.group, in first-declaration order; actions withoutgroupsit under 「常规」; everyvariant: 'danger'action is drawn in the red-bordered 「危险操作」 section at the bottom whatever itsgroup.label, itsdescription(when declared) as the muted line, its button; params dialog and confirm as everywhere else.Authored pages — a correction to delta 1 (6248562326). Delta 1 said the environment page's tab 「appears by itself after the objectui pin」. That overstated it: the environment page is an authored (
kind: 'slotted') page, and option A leaves authored pages untouched — the synthesizer never injects a tab into a page an author wrote. What an authored page gets is the same rendering as one block: the seat proposesrecord:quick_actionswith a newlayout: 'sections'mode (location: 'record_section', noactionNames), which draws everyrecord_sectionaction of the object grouped bygroupwith the danger zone, exactly what the default tab draws.actionNamesomitted today renders an empty bar (measured,component.zod.ts:2347), so the mode must be explicit, never a silent fallback. Under A, cloud#2916's environment page therefore authors a 「设置」 tab containing that one block (instead of five hand-named blocks), andsys_user's page keeps its own 「安全」 tab as is or swaps to the one block — the author's choice.What this adds to option A's contract list:
ActionDefinition.group(new key) ·record:quick_actions.layout: 'sections'(new value on an existing block) · the synthesizer rule (1)–(4) · therecord_headercount lint. Workload unchanged: spec S, objectui M.os-decision-facets
record_section动作),无新开关;手写页与默认页共用同一块渲染。layout: 'sections'必须显式写,不把今天「actionNames缺省即空」的实测行为改成静默回退;页签对每个查看者按visible与权限过滤,与页头同一条路。record_section动作的对象零变化。自检:只看①选 A;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 18, 2026
席位意见(增量 3 · 元数据怎么改;两个更省的设计 D、E;推荐改为 E)· director seat (summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax) · on batch #365 item 1, 2026-10-18T10:16ZThe maintainer's words in this session's chat, verbatim: 「具体解释 对象至少有一个动作的 locations 含 record_section。元数据有些什么调整。这个开发感觉有点太牵强了,还有更好的设计方案吗」 and then 「如果保持现状,而是优化更多按钮的下拉菜单呢」. Three answers.
1. What option A changes in metadata, concretely (
packages/service-tenant/src/objects/sys-environment.object.ts, 23 of the 24 header actions, one edit each; the three that stay in the header — 打开, 安装应用, 升级 — untouched):「At least one action with
record_section」 means: the moment any action on the object says 「I live in a body section」, the default page has something to put in a 「设置」 tab and draws one; the page itself declares nothing. The seat agrees with the maintainer's reading: that is an inferred switch (a tab triggered by a location value plus a danger zone inferred fromvariant), and the mechanism needs four pieces (group, the synthesizer rule, alayout: 'sections'block mode, a lint) for one effect.2. Option D — an explicit object-level declaration, the clean version of the generic tab. One new object key beside
actionsandlistViews, read by the default page and by one block:plus the same
locationsedit as A (record_header→record_section) on the moved actions, which every design needs: an action that keepsrecord_headerstays in the header fold. Trigger: the key is present. Danger zone: declared (danger: true), not inferred. Captions possible (descriptionon a section). Validator: every named action exists on the object and carriesrecord_section, no action in two sections, no empty section. Authored pages place one block,record:settings, which draws the declaration; the default page puts that block in a 「设置」 tab. Contract: one object key, one block. Studio can edit it as a list, likelistViews. The mock on the canvas stands for D as drawn (the section labels become the author's).3. Option E — keep the status quo, improve the ⋯ menu. The maintainer's question. Actions gain
group(the same key as A); nothing else moves:locationsstay, the header fold stays (maxVisible3,orderpicks the inline three). objectui's ⋯ menu renderer (containers.tsx2066, today three bands) draws a small heading pergroupwith separators, keeps the console's injected 分享 / 收藏 in their own band, and puts everyvariant: 'danger'action last in red. No tab, no block, no synthesizer rule. Drawn on the canvas (third row): https://claude.ai/artifact/WEQfNGTRQw19uqBVH3eqKb — a running free-plan environment shows about 16 items aftervisible. Honest limit: a grouped menu reads well up to roughly a dozen items; above that a page reads better. So E alone does not make the environment page good — cloud#3001's authored page (ruled A) remains the answer there, because that page also needs current values (plan, hostname, SSO state) that no menu carries. E is the floor every object gets; it reaches hotcrm's objects (≤3 header actions today) as soon as they addgroup, with no page work.Seat's recommendation, revised: E now; cloud#3001 as ruled; D parked; A withdrawn. E is the smallest platform change that helps every object (one spec key, one renderer change, one objectui pin), consistent with 「平台相关的功能,应该尽量先改平台」. When a second object outgrows a menu, the generic tab is D, not A: an explicit declaration beats an inferred switch. The
record_headercount lint rides whichever letter.Workload: E = spec S + objectui S · D = spec S + objectui M · A = spec S + objectui M.
Execution: E → the director files an objectstack spec card p2
pm:queue(ActionDefinition.group, docs) and an objectui card p2pm:blockedon the spec pin (grouped ⋯ menu); cloud#2916 unchanged. D → spec card (settingskey +record:settingsblock) and objectui card; cloud#2916's 「设置」 tab becomes the one block after the pins. A → as delta 1 and 2.os-decision-facets
自检:只看①选 E;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 18, 2026
席位意见(增量 4 · 下拉菜单要不要分组;不分组时的排序)· director seat (summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax) · on batch #365 item 1, 2026-10-18T10:36ZThe maintainer's words in this session's chat after the letter 「24277 E;3002 A」, verbatim: 「24277 下拉菜单需要加分组吗?」 and 「如果不加分组会有排序的概念吗」. The letter E is held until the maintainer picks one of the two readings below; no E write has been made.
What the ⋯ menu already does, read at objectui
a8560b228527(packages/components/src/renderers/layout/containers.tsx):orderascending (unset = 0; lower = more prominent),variant: 'primary'as a tie-break, then registration order, stable (objectui#2339 / feat(plugin-auth): reject breached passwords via HIBP (ADR-0069 D1, P1) #2361; specaction.zod.ts:1314–:1327). Every band of the menu keeps that order. So withoutgroup, an object already controls the menu's sequence byorderon its actions, and by declaration order when it sets none.a?.variant === 'destructive' || a?.name === 'sys_delete'. The spec's actionvariantenum isprimary | secondary | danger | ghost | link—danger, notdestructive— and the environment's archive and delete declarevariant: 'danger'(sys-environment.object.ts:1162,:1221). Nothing on the header path mapsdangertodestructive(onlyDeclaredActionsBar.tsx:409does, for its own buttons). So an app's own danger actions most likely sit in band 1, unmarked, and only the console's 删除 reaches the red band. That is an objectui fix with no spec change.The two readings of E:
dangerspelling (item 3), pinned by a test; otherwise the menu stays as it is. Objects that want a better menu setorderand declarevariant: 'danger'— both already in the contract. Work: objectui S; nothing waits on a spec pin. A long menu stays a long list, which for the environment is moot (cloud#3001 A gives it an authored page).ActionDefinition.groupand the sectioned menu (delta 3, the third-row artboard). Work: spec S + objectui S; two cards, one pin wait for the renderer. Today no object both has a long menu and lacks an authored page, so the headings have no consumer yet.Seat's reading: E1 now;
groupwhen a consumer appears. The answer to 「需要加分组吗」 is therefore 「现在不需要」, and to 「不加分组会有排序的概念吗」 is 「有,已经在平台里」.os-decision-facets
order、variant: 'danger'),缺的只是渲染器认错拼写;group等有消费方再加。danger动作的对象。danger、渲染器查destructive」这类一词两拼的错。自检:只看①选 E1;②③④ 是否翻转:否。
objectstack-fleet commentedon Oct 18, 2026
Ruling: batch #365 item 1 · letter E1 · maintainer 「24277 E」 then 「E1」 2026-10-18T12:12Z
Director seat, summon #43 continuation 5,
session_01QJC7i981TSs9L1CGJK59Ax, on the director's own card (A recommended at filing; revised to E in delta 3, 6248654513; E split into E1 / E2 in delta 4, 6248833927, after the maintainer's 「24277 下拉菜单需要加分组吗?」 and 「如果不加分组会有排序的概念吗」), the deltas 6248562326 / 6248585323 / 6248654513 / 6248833927 and the canvas https://claude.ai/artifact/WEQfNGTRQw19uqBVH3eqKb (opening marker #12708, comment 6247058702). The maintainer's words in this session's chat: 「24277 E;3002 A」, then 「E1」.What the ruling binds (letter E1 — keep the status quo; no new spec key; the ⋯ menu's existing order and bands do the work):
group, no settings-tab key, no block mode, no synthesizer rule, no lint. A many-action object is laid out with what the contract already has —order(lower = more prominent;primarytie-break; declaration order otherwise),record_morefor actions that never need an inline slot,variant: 'danger'for the red band.dangerspelling (delta 4 item 3, read from source, measured first) — console: the record ⋯ overflow menu's destructive band honours the spec'svariant: 'danger'(objectstack#24277 ruling E1) objectui#12961, p2pm:queue. Nothing else in the menu changes.groupand the sectioned menu, the third-row artboard), D (an explicit object-levelsettingsdeclaration) and A (the inferred default tab) stay on this record for the day an object both has a long menu and lacks an authored page; D over A then.Not taken: E2 (no consumer today), A (an inferred switch), D (parked).
Workload of the letter: objectui S.
Execution: objectstack-ai/objectui#12961 filed in this act; this card leaves
needs-user-decisionand closescompletedpointing at it.objectstack-fleet commentedon Oct 18, 2026
Closed
completed· ruled E1 at 6249771678 (batch #365 item 1, the maintainer's 「24277 E」 then 「E1」) · director seat (summon #43 continuation 5,session_01QJC7i981TSs9L1CGJK59Ax) · 2026-10-18T12:14ZExecution: objectstack-ai/objectui#12961 (the ⋯ menu's red band honours
variant: 'danger'; p2pm:queue). No spec change. E2, D and A stay on this record for a second many-action object. cloud#2916 and cloud#3001 are unaffected.