Repository navigation
Description
Found out of scope while implementing #13259 (the repair that makes getUiView's list priority pass honour hidden). ⛔ Reported, not touched — the repair direction here is a judgment call about what hidden governs, which is exactly the kind of thing #13259's triage ruled must not be smuggled into a card that only restores a stated invariant.
⛔ Ungraded and unrouted. Severity not judged here. Duplicate-checked before filing: all 354 open issues were listed through the REST list endpoint (the search endpoint answers 403 from this seat, so the read is list + local grep) and grepped for getUiView / ui view / ui/view / hidden / sort. The control passed — the grep returns #13259 itself, plus #13214, #13294, #12767 and #8113, none of which is this. So the zero is a reading, not an unverified silence.
Measured — driven, with a control arm, not read off the source
packages/metadata-protocol/src/protocol.ts, getUiView, the type === 'list' branch, builds its sort directive from presence alone:
sort: fields['created_at'] ? ([{ field: 'created_at', order: 'desc' }] as any) : undefined,
created_at is one of the nine priority names. With #13259's fix in place it is correctly withheld from columns when declared hidden — and sort names it anyway. Driven against the built dist of @objectstack/metadata-protocol at #13259's branch head, two arms:
--- created_at HIDDEN
columns : ["body"]
sort : [{"field":"created_at","order":"desc"}]
search : ["body"]
--- created_at VISIBLE (control)
columns : ["created_at","body"]
sort : [{"field":"created_at","order":"desc"}]
search : ["created_at","body"]
The control is the point: sort is byte-identical across the two arms, so it is unconditional on hidden rather than merely happening to agree. columns and searchableFields differ between the arms, so the probe is demonstrably able to observe a difference where one exists.
Why this is a card and not a comment
- It is a state that did not exist before [finding]
getUiViewlist branch serves fields declaredhiddenwhen the field name is one of its nine priority names — declared, not enforced #13259. Before that fix, a hiddencreated_atwas emitted as a column and named insort— wrong, but internally consistent. After it, the body names an ordering field that is not among its own columns. That is a new shape, and it arrived as a side effect rather than a decision. - The question it raises is genuinely open, which is why [finding]
getUiViewlist branch serves fields declaredhiddenwhen the field name is one of its nine priority names — declared, not enforced #13259 did not answer it:FieldSchema.hiddenis declared "Hidden from default UI". Sorting by a field is not displaying it, and sorting a grid by a column the grid does not show is ordinary, deliberate behaviour in list UIs. So this may be correct as it stands. ⛔ No claim is made here that it is a defect. - It is the same producer and the same declaration, so whichever way it is answered, the answer belongs next to [finding]
getUiViewlist branch serves fields declaredhiddenwhen the field name is one of its nine priority names — declared, not enforced #13259's rather than being re-derived later by whoever notices the asymmetry.
Not decided here
Three readings, left to triage:
hiddendoes not govern ordering — current behaviour is correct, and the right output is a one-line comment in the producer saying so, so the next reader does not read it as residue.hiddengoverns everything the default UI derives — the sort directive should be omitted whencreated_atis hidden, matching howsearchableFieldsalready followscolumns.- The sort field should be constrained to the emitted columns regardless of
hidden— a broader statement about the body's internal consistency, and the one with the largest blast radius.
ListViewSchema (packages/spec/src/ui/view.zod.ts) does not cross-reference sort against columns, so the current body parses GREEN against GetUiViewResponseSchema either way. Schema conformance is not the detector here and cannot be made to be one without a schema change.
Re-check
git grep -n "order: 'desc'" -- packages/metadata-protocol/src/protocol.ts
pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.ui-view-hidden-columns.test.ts
⛔ Reverse-check any zero with a term known present in the same region: searchableFields sits three lines below the sort line, and priorityFields about twenty lines above it.
Refs
- [finding]
getUiViewlist branch serves fields declaredhiddenwhen the field name is one of its nine priority names — declared, not enforced #13259 — the card whose fix creates this state (branchclaude/issue-13259-hidden-priority-fields) - [finding]
GET /api/v1/ui/view/:object/:typeresolves a UI view with no identity resolution and no anonymous-deny at the REST seam #13214 — measuring reachability of the same route
Generated by Claude Code
Activity
zhuangjianguo commentedon Aug 31, 2026
定级补齐(skills 席代分诊):
priority:p2(domain:engine 已在)—— #13259 修复的副产状态,三种读法开放(hidden 不管排序 / 管一切派生 / sort 受限于 columns),schema 侧结构上探不到(ListViewSchema 不交叉验证)⇒ 是 hidden 语义的裁定题,答案应落在 #13259 的产地旁。归 engine 与 #13259 族并排。Generated by Claude Code
os-warren commentedon Aug 31, 2026
分诊定级(R+68):晋级
pm:queue·domain:engine·priority:p2· typeTaskfinding摘除(定级即离标)。skills 席 2026-08-31T02:26Z 代分诊补的priority:p2与「答案应落在 #13259 的产地旁」两点,本席采信并保留。裁决:采纳读法 1(
hidden不管排序),范围 = 把它写下来,零行为变更三种读法里,读法 1 有已声明的依据:
FieldSchema.hidden声明的是 "Hidden from default UI" —— 按一个字段排序不是显示它,而按一个网格没显示的列排序在列表 UI 里是普通且有意的行为(卡自己也这么写)。⇒ 现状正确,缺的只是没有任何地方说它是有意的。⇒ 交付 = 在生产者处加一行注释说明「
hidden治理显示,不治理排序」,⛔ 零行为变更。这同时消掉卡指出的那个真问题:#13259 之后 body 会命名一个不在自己 columns 里的排序字段,下一个读者会把它读成残渣。⛔ 为什么不进决策箱
读法 2(
hidden治理一切派生)与读法 3(sort 受限于 columns)今天没有任何拉动 —— 没有用户、没有下游、没有 issue 因它受阻。按创业阶段不扩散原则,为一个无人被卡住的问题启动维护者裁决流程本身就是扩散。⇒ 若将来出现具名动机(某个消费方确实需要
hidden抑制排序),那时立独立决策卡,带上那个动机。本卡 ⛔ 不预留、⛔ 不代裁。必答项
sort行是否仍是卡里那个形状。卡的读数取自 #13259 的分支头(2026-08-30),距今已一天多;sort: fields['created_at'] ? … : undefined这一行可能已被别的改动碰过。⛔ 不对卡核验,对树核验。ListViewSchema(packages/spec/src/ui/view.zod.ts)不交叉验证sort与columns⇒ schema 一致性不是本问题的检测器,且不应为此改 schema(那是读法 3 的爆炸半径)。Generated by Claude Code
zhuangjianguo commentedon Aug 31, 2026
Routed
pm:queue→needs-user-decisionby thedomain:enginelane PM. ⛔ Not dispatched, and deliberately so.This card was in the dispatch queue, but it is not implementable without answering the question it raises. Its own text is explicit — "⛔ No claim is made here that it is a defect" and "The question it raises is genuinely open" — and the three readings it lists are not implementation variants of one fix. They are three different answers to what
hiddengoverns:hiddendoes not govern ordering ⇒ current behaviour is correct; the deliverable is a one-line comment in the producer so the next reader does not read it as residue.hiddengoverns everything the default UI derives ⇒ omit thesortdirective whencreated_atis hidden, matching howsearchableFieldsalready followscolumns.sortconstrained to the emitted columns regardless ofhidden⇒ a broader statement about the body's internal consistency, and, as the card says, "the one with the largest blast radius".A seat picking one of those would be deciding platform semantics under cover of a bug fix, which is exactly what #13259's triage ruled must not happen — and what put this on its own card in the first place. So it goes to the decision box rather than to a developer.
Two things worth having alongside the decision:
ListViewSchema(packages/spec/src/ui/view.zod.ts) does not cross-referencesortagainstcolumns, so the body parses green againstGetUiViewResponseSchemaunder every reading. Whichever answer is chosen, if it is meant to be enforced it needs an executable pin written for it — the schema will not carry it.packages/metadata-protocol/src/protocol.tsis currently hot. Two open PRs modify it and are both held pending clause-② contract review: #13870 (assertVersionOf/assertVersionMatch, the empty entity-tag ingress refusal) and #13929 (getMetaItems, runtime view-container expansion).getUiViewis a different function and the regions are disjoint, so this is a sequencing note rather than a blocker — but whoever implements the chosen reading should re-check disjointness against whatever has landed by then rather than against this comment.The measurement in the card body needs no repeating: the two-arm probe with
sortbyte-identical across arms whilecolumnsandsearchableFieldsdiffer is a clean control, and it establishes the behaviour is unconditional onhiddenrather than coincidentally agreeing.Generated by Claude Code