Skip to content

feat(spec)!: promote userFilters.allowAddTab,随后收紧 UserFiltersSchema (#5073) - #5237

Merged
xuyushun441-sys merged 5 commits into
mainfrom
claude/issue-5073-userfilters-allowaddtab
Aug 4, 2026
Merged

feat(spec)!: promote userFilters.allowAddTab,随后收紧 UserFiltersSchema (#5073)#5237
xuyushun441-sys merged 5 commits into
mainfrom
claude/issue-5073-userfilters-allowaddtab

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #5073

按本单已记录的维护者裁决 A(2026-08-04)执行:promote allowAddTab 进 spec 的 UserFiltersSchema,同 PR 完成该 schema 的收紧。没有重新论证选项,也没有留中间态。

实施前重新实测了裁决所依赖的前提,与 issue 记载一致:HEAD 上 UserFiltersSchema 仍是 z.object(strip),spec 声明 {element, fields, tabs, showAllRecords},objectui 的 packages/types/src/zod/objectql.zod.ts:265 声明 {element, fields, tabs, allowAddTab, showAllRecords} —— 两边差集恰好就 allowAddTab 一个键

为什么之前不能直接关

objectui 的渲染器真读 config.allowAddTab 并据此渲染「新增 tab」入口(plugin-list/src/UserFilters.tsx:182 / :742)。而 saveMetaItemsafeParse 校验后原样存原始 body(丢弃 parsed.data,好让 Studio 辅助键活过往返),所以被 strip 掉的只是那份被丢弃的解析结果 —— 存储里键还在,渲染器读得到,能力今天是工作的。直接收紧不是「把静默失效变响亮」(战役的授权),而是把已发布、在用的配置变成 422,且 422 会点名一个作者本来写对了的键 —— finding 7 的形状。

落地内容

1. allowAddTab 声明进 UserFiltersSchema(additive)。 能力从此可从契约被发现:JSON Schema、Studio SchemaForm、参考文档、AI 作者都看得到,而不是只存在于一个 React 文件里。

.describe() 刻意收窄到渲染器真做的事 —— 「渲染出新增 tab 的入口」,不承诺点击后能创建预设。因为实测那个按钮没有 click handler(同一个 return 里预设 tab 有 onClick,它没有),写成「终端用户可以新增预设」就是 PD#10 的「宣传运行时不交付的能力」。渲染器那半边已另行立案 #5236(unassigned),不在本 PR 范围。

2. UserFiltersSchema 收紧(z.objectstrictObject),未知键从静默丢弃变为带处方的拒绝。

3. 批 6e 的翻转如期发生,并且补上了它自己制造的坑。 ObjectUserFiltersSchemaUserFiltersSchema.omit() 派生,.omit() 继承基类姿态,所以基类一关,对象视图上的 tabs / showAllRecords / allowAddTab 从「丢弃」翻成「拒绝」—— 这个翻转是想要的:CLI lint(packages/lint/src/validate-list-view-mode.ts)早就在报同一个配置,此前两扇门互相矛盾,现在一致。

.omit() 连基类的错误映射一起继承,而那份 knownKeys 是从基类形状读的、仍然含被 omit 掉的键。实测(改之前):

{ tab: 1 }  →  Unrecognized key(s) on these user filters: `tab`. … Did you mean `tab` → `tabs`?

把作者指向这个形状唯一拒绝的键 —— 用 finding 7 的修复制造了一次 finding 7。而这次拒绝正是本 PR 造出来的,所以消息也是本 PR 的责任。对象变体改为携带自己的映射,候选池按 omit 后的形状构建(形状仍由 .omit() 派生,#2231 的 derive-by-reference 不变),三个 page-only 键各带 guidance 指向 listViews。现在:

{ tabs: [] }  →  … `tabs` presets are page-only: …Define each preset as a named entry
                   under the object's `listViews` instead — every one renders as a segmented tab.
{ tab: 1 }    →  …(不再建议 `tabs`)

未被打扰的既有姿态

台账(#5220 之后的新纪律)

  • 数字:gen:strictness-ledger 整体重生成,未手改一个数。ui/ sites 198→199、strict 117→119、strip 76→75;view.zod.ts sites 50→51、strip 6→5(新增的那个 site 是对象变体改用 strictObject( 的调用,它本身是 strict)。
  • 散文:只改 view.zod.ts 那一行(feat(spec,lint): 声明 chart segment drill —— ChartDrillDownSchema,挂在真正读它的那一层 (#5022) #5221 动的是 chart.zod.ts 行,不同行,无冲突)。Class 按格式契约的词表从 mixed · 6 authorable 改为 mixed · 5 authorable,并把 UserFiltersSchema 从「still open」改写成结案记录(含裁定依据、describe 收窄的理由、以及上面那个继承错误映射的坑)。
  • check:strictness-ledger 绿。

破坏性 + 迁移

.changeset/user-filters-allow-add-tab-promote-and-close.md(major;.changeset/pre.json 已确认 mode:pre / tag:rc,v17 窗口开着),正文带 FROM → TO:

  1. userFilters 上的未知键 → 删除或改成想表达的已声明键(错误信息点名并给候选)。allowAddTab 本身不需要迁移 —— 它现在是合法键。
  2. 对象视图的 userFilters.tabs → 对象的 listViews 具名条目;showAllRecords → 默认列表视图即「全部记录」入口;allowAddTab → ViewTabBar 自带的新增控件。

验证

pnpm --filter @objectstack/spec check:generated   →  ✓ All 9 generated artifacts are up to date.
packages/spec        vitest run                   →  306 files / 7834 tests passed
packages/lint        test                         →  57 files / 1184 tests passed
packages/objectql    test                         →  115 files / 1839 tests passed
packages/metadata-protocol test                   →  39 files / 348 tests passed
typecheck (spec + lint)                           →  Done

生成物经 check:generated --fix 只重生成被证明陈旧的 2 个(gen:docs / gen:strictness-ledger);authorable-surface.json 由 build 带出,新增一行 ui/UserFilters:allowAddTab。已合入 origin/main(未 rebase / 未 force-push),合并后重跑上述闸门仍全绿。

顺带发现(未在本 PR 修)

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9


Generated by Claude Code

claude added 2 commits August 4, 2026 11:38
…sSchema (#5073)

`UserFiltersSchema` was the last shape #4001 批 18 left open in `ui/view.zod.ts`
whose blocker was a capability question rather than a strictness one.

objectui's renderer reads `config.allowAddTab` and renders an add-tab affordance
from it (`plugin-list/src/UserFilters.tsx:182`/`:742`), and its own schema
declared the key — the difference between the two shapes was exactly that one
key. Because `saveMetaItem` validates with `safeParse` but persists the ORIGINAL
body, the stripped key still reached the renderer, so the capability worked.
Closing without declaring it would have 422'd a shipped config with a message
naming a key the author was right to write (finding 7), not converted a silent
failure into a loud one.

Maintainer adjudication (2026-08-04) chose promote-then-close in one PR:

- `allowAddTab` is DECLARED, so the capability is discoverable from the contract
  (JSON Schema / Studio SchemaForm / AI authors) instead of one React file. The
  `.describe()` is scoped to what the renderer really does — it renders the
  affordance; the button carries no click handler today, filed separately, and
  promising more would be PD#10.
- `UserFiltersSchema` closes behind it, in the same change, so there is no state
  where the key is declared but its neighbours still vanish silently.

Closing flips `ObjectUserFiltersSchema` (`.omit()`ed off the base, and `.omit()`
inherits posture) from dropping the page-only keys to rejecting them — 批 6e's
question, and the wanted answer: the CLI lint already reported them, so the two
doors now agree. But `.omit()` also inherits the base's error map, whose
`knownKeys` still listed the omitted keys: measured on the flip, `tab` was
answered "Did you mean `tab` -> `tabs`?", steering the author at the one key
that surface refuses. The object variant now builds its map over the OMITTED
shape (shape still derived by `.omit()`, so #2231 holds) with `guidance`
pointing all three page-only keys at `listViews`.

Ledger: view.zod.ts Class 6 -> 5 authorable; counts regenerated via
`gen:strictness-ledger` (ui/ strict 117 -> 119, strip 76 -> 75, sites 50 -> 51).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 4, 2026 11:55am

Request Review

claude added 2 commits August 4, 2026 11:47
#5221)

The merge with origin/main brought in #5221 (`chart.zod.ts` gains
`ChartDrillDownSchema`). Git text-merged the GENERATED counts file cleanly and
WRONGLY — the ledger's own documented failure mode: each side's row deltas do
not overlap, so the subtotals merge clean while naming a tree neither branch
ever had (483/199, missing #5221's site).

Regenerated via `gen:strictness-ledger` from the merged tree, which is the only
state in which the numbers are right: 484 sites / ui 200, strict 244 / ui 120.
Both hand-written prose rows survive untouched — `view.zod.ts` (this PR) and
`chart.zod.ts` (#5221) are different rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling size/m labels Aug 4, 2026
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…er row

Both spots described the add-tab button's missing click handler as "filed
separately" without the number, which is the shape AGENTS.md #13 warns about —
a decision nobody can find. Now the reader of either surface can reach the
evidence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
@xuyushun441-sys
xuyushun441-sys marked this pull request as ready for review August 4, 2026 12:24
@xuyushun441-sys
xuyushun441-sys added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit c183a12 Aug 4, 2026
25 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the claude/issue-5073-userfilters-allowaddtab branch August 4, 2026 12:36
xuyushun441-sys pushed a commit that referenced this pull request Aug 4, 2026
…-1) → 15

合并队列以 MERGE_CONFLICT 踢出后重新同步 origin/main(merge,非 rebase)。
#5237(#5073 allowAddTab)与本批从两个不同目录改这份台账,散文行不重叠,
git 干净合并;数字零手改,counts.md 按 merge=os-regen 从合并后的树整体重生成。

重算结果 15 = `ui/` 7 → 6(#5237)+ `data/` 22 → 9(批 20),两边分支都没写过
这个数——正是 #5107 拆分要达到的效果。这一条也补进 data/ 段落:它是第十二次
实例,也是第一次没让任何人付出代价。

#5237 对 `ui/app.zod.ts` 的改动(`apis` 墓碑文案)与本批的 BaseNavItemSchema
判定互不相干,两侧都在;它对 scripts/strictness-ledger.test.ts 的 view 计数
5 也是它自己的领地。

门:spec 308 文件 / 7929 测试通过;typecheck 通过;check:generated 9/9;
check:strictness-ledger 通过(484 站点,authorable 剩 15)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ehu85kbvMcrNTUJjwxvLJ9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

2 participants