Skip to content

refactor(10 packages)!: burn ledger batch 7 — 14 symbols stop wearing spec names - #3224

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-3161-spec-symbol-batch7
Aug 2, 2026
Merged

refactor(10 packages)!: burn ledger batch 7 — 14 symbols stop wearing spec names#3224
os-zhuang merged 1 commit into
mainfrom
claude/issue-3161-spec-symbol-batch7

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#3161. objectstack#4115 台账燃尽第 7/8 批(长尾:9 包 14 符号)。

台账 17 条 / 11 包 → 3 条 / 1 包。十个包整包退出台账,只剩批次 8(objectui#3162,等 objectstack#4171)的 @object-ui/types 三条,逐字节未动。

判决一览(逐符号,不按簇)

符号处置spec 的同名导出到底是什么
fieldsisFileIdTokenre-export同一个函数——本地是它逐字符相同的副本
fieldsFieldWidgetProps改名 FieldWidgetComponentProps字段 widget 插件的声明式 props 契约(zod)
layoutPageHeaderProps改名 PageHeaderComponentProps授权态 page:header 节点(zod schema)
layoutPage改名 PageNodeRenderer授权态页面文档(name/label/regions)
plugin-detailFeedFilterModere-export同一个四成员枚举
plugin-detailObjectFieldLike改名 ObjectDefFieldLikei18n translateObject 的鸭子类型(带 [key: string]: any)
plugin-gridColumnSummaryConfig改名 ColumnSummarySetting + 派生只是 ListColumn.summary对象形态,本地那个是整个联合
plugin-gridisMultiValueField改名 hasMultiValueShape + 委托要求 type 必填的分类函数
collaborationRealtimeConfig改名 RealtimeSubscriptionConfig应用级 realtime 声明(transport/subscriptions[])
plugin-chartsChartConfig改名 ChartContainerConfig授权态图表文档(type/xAxis/series)
plugin-formFormSection改名 FormSectionContainer授权态表单分节元数据(name/pane/fields)
providersTheme改名 ThemePreference + 派生整份主题文档(colors/typography)
runnerApp改名 RunnerApp应用元数据类型 +App.create() 构造器
sdui-parserValidationResult改名 ManifestValidationResult插件清单校验结果({valid, errors?, warnings?})

跨包定名照批次 3/4 的判决执行,未另起炉灶:PageHeaderComponentProps 沿用 app-shell(objectui#3169),ManifestValidationResult 沿用 < 被校验的东西 >Validation< Error|Result > 约定(objectstack#4115 评论、@object-ui/core 批次 4)。

值得写进方法学的四件事

一、Theme 的预判两个方向都不对 —— 它既不是「已解析的主题」也不是「授权 JSON」

跟踪评论建议「先问 providers 的 Theme 是运行时已解析的主题还是授权 JSON;两种情况都不需要新方言名」。实测是第三种:它是 Theme['mode'] 顶着 Theme 的名字——一个字符串联合,站在 spec 一整份文档的位置上。这比一份漂了的副本更误导:下一个 session 读到 @object-ui/providersTheme 会得出「ObjectStack 的主题是个字符串」的结论。

而 spec 自己给这个概念的名字 ThemeMode也是 spec 导出(又一次印证批次 3 的「新名字必须先跑守卫」——显而易见的名字恰恰最容易已被占用),何况本地还多一个 'system'(pre-spec 拼写,用户 localStorage 里存着的)。所以落在 ThemePreference,并且派生:三个真实模式取自 spec 枚举,'system' 显式留作本地成员。

二、ColumnSummaryConfig 是一种新形态:顶着 spec 的名字,却是一个更宽的类型

spec 的 ColumnSummaryConfig 只是 ListColumn.summary对象形态({type, field?}),简写形态 'sum' 在 spec 里叫 ColumnSummary,两者的并集才是 summary 接受的东西。本地那个声明顶着 ColumnSummaryConfig 的名字装了整个并集——同一个名字,一边接受 'sum',另一边拒绝。没有任何测试能发现它,因为两边各自都是「对的」,只是在说不同的事。

改名为 ColumnSummarySetting,并直接取 NonNullable< ListColumn['summary'] >——由 spec 自己定义它接受几种形态。顺带把旁边那份守卫看不见的手抄件也拆了:十一个成员的聚合联合原本是手写的,注释承诺「与 spec 枚举保持同步」而没有任何东西执行这句承诺;现在它就是 spec 的枚举,于是 TYPE_LABELS(一张 Record< ColumnSummaryType, string > 全键映射)变成了报告差异的地方——spec 新增一个聚合,得到的是一条指名道姓的编译错误,而不是一个渲染成空白的页脚单元格。

三、又一次「注释声称是镜像,实际是逐字副本」——而这次副本的内容是一个线上决定

fieldsisFileIdToken 与 spec 的实现逐字符相同(/^[A-Za-z0-9_-]{1,64}$/),注释写着 "Mirrors the platform's isFileIdToken"。与批次 3 的 isAggregatedViewContainer 同型:任何值比较、任何行为断言都会全绿,只有引用同一性能区分。测试里的闸门因此是 expect(isFileIdToken).toBe(specIsFileIdToken)

为什么这一份特别值得共享而不是复制:那个正则是线上契约。服务端哪天放宽它(比如 id 超过 64 字符),本地副本仍按老边界回答「这不是一个引用」,于是 widget 会朝一个期待引用的后端提交 legacy inline blob——症状是缩略图坏掉,离一个正则十万八千里。

四、批次 6 的第一条方法学在这批双向各命中一次

  • 正向:issue 正文预判 Page(layout)与 App(runner)「预计走改名或 ALLOW」——实测走改名成立,但理由与预判不同。
  • 反向:预判 isFileIdToken / isMultiValueField「若语义一致则 re-export」——一个 re-export、一个不能isMultiValueField 的本地版本与 spec 版本前置条件不同:spec 的要求 type 必填,本地那个的调用点是 objectFields?.[p.name],例行传 undefined。共享一个名字、却有不同的前置条件,比一份普通的 fork 更危险——两个模块之间调错 import 仍然能编译,只在「字段不存在」这条路径上改变行为。所以改名 hasMultiValueShape,并把分类本身委托给 spec 的函数(原来是拿 MULTI_OPTION_TYPES / MULTI_CAPABLE_TYPES 自己重推那两行——一个自称 "consumed instead of restated" 的 helper 最后还是把规则 restate 了)。

另外:ChartConfig 是本批唯一一个已经在读的代码里造成歧义的碰撞

AdvancedChartImpl.tsxconfig?: ChartConfig 指的是 shadcn 的按序列样式表,而同一个文件上下几行的注释写着 "Spec ChartConfig.yAxis"、"Spec ChartConfig.showLegend" 指的是 spec 的图表文档。一个标识符、两个概念、一个文件。改名之后那些注释才第一次是对的。

派生用的是哪一侧

ThemePreferenceColumnSummary* 都取授权侧ThemeModeSchema 通过 schema 自己的 _zod 载体读(与 packages/react/src/spec-input.ts、批次 6 的 ViewTab 同一手法),@object-ui/providers 因此不需要 zod 依赖。

批次 6 的第 3 条点名 FormSection / ChartConfig / RealtimeConfig 是同一陷阱的候选——三条实测都不适用,理由各不相同:ChartConfig / RealtimeConfig 与 spec 的同名导出根本是两个概念(没有可派生的东西);FormSection 确实带两个 .default(),但拦住绑定的不是它们,而是 columns(见下)。

为什么 FormSection 走改名而不是批次 6 给 ListView 的 ALLOW

判据不是「组件一律豁免」(批次 5 把 Field 改成了 FieldContainer),而是:这份声明会不会被下一个 session 读作 spec 那个概念的权威形状ListViewProps.schema 把 spec 派生的元数据整个当作一个 prop 收下,所以那个渲染器什么形状都没声明;FormSectionContainerProps 则是把同一份元数据摊平成一个个 prop(label / description / collapsible / collapsed / columns),少了 fieldsnamepanevisibleWhen,多了渲染专用开关——这正是一份对手形状。而且这个词在本仓自己的文档里已经归 spec 那个概念了(plugin-form/README.md 与插件文档都写 "spec FormSection.pane",@object-ui/types 把 spec 的类型 re-export 成 SpecFormSection),文件头还写着 "Aligns with @objectstack/spec FormSection"——守卫文件头所说的「给下一个 session 埋的前提」。

columns 刻意绑定 spec 的键:spec 的授权侧接受 '1' | 1 | '2' | 2 | … 并在自己的 pipe 里把字符串规约掉,而这里的 gridCols 按数字索引——绑过来会让组件收到 '2' 然后静默渲染成一列。规约属于解析授权元数据的接缝,不属于容器的 props。测试把这条钉住了:spec 哪天退掉字符串形态,钉扎报红,这个键就可以绑了。

顺带记录的、未修的东西(都已单独立单)

  • objectui#3221FieldWidgetComponentProps[key: string]: any。这是本批最锋利的一处:索引签名让这个类型对每个未声明的键都回答 any,所以 spec 声明的 required / error 在本地「已经存在」——读它们合法、类型 any、运行时恒 undefined。一个声称拥有全部键的类型永远不可能被报告「缺了某个键」(objectstack#4075 的机制)。**parity 测试对这个符号原理上无效,守卫是唯一探测器。**测试里三条钉扎就是为了在 FieldWidgetComponentProps[key: string]: any 让每个 widget prop 检查失效 #3221 删掉索引签名那天报红。
  • objectui#3222 — spec 的 widget props 契约把校验消息槽叫 error,objectui 的 widget 全部读 errorMessage。照着 spec 写的第三方 widget 放进来不显示校验消息,且无人报错。方向需要拍板(objectui 跟随 / spec 跟随 / 按 ADR-0049 退掉这份没人执行的 schema),所以只记录。
  • objectui#3223layoutPageNodeRenderer 没有被任何地方注册也没有调用点(page 键归 @object-ui/componentsPageRenderer),props 还是 … & any——与 any 求交会吸收整个类型,是批次 6 记录的三种「注销编译期检查」写法之外的第四种。删还是接线,需要知道意图的人拍板。
  • spec 的 RealtimeConfigSchema 是 passthrough(z.core.$loose),所以 keyof SpecRealtimeConfigstring,任何 K extends keyof Spec 探针一律回答 true——批次 5 方法学第 7 条在 spec 侧的又一个实例(同批 ObjectFieldLike 是同类)。collaboration 的「两边零共享键」因此改成对着 spec 自己的 schema shape 在运行时比对,并用 Record< keyof Local, true > 让 tsc 保证本地键列表穷尽。未提 issue:这可能是 spec 为 transport 前向兼容的有意选择。

验证

  • 守卫:绿(11 declared dialects, 3 untriaged collisions in 1 packages)。--ledger 重生成后逐条 set-diff:removed 恰为本批 14 条、added为空、其余包(@object-ui/types,批次 8)逐字节不变
  • type-check:turbo type-check78/78(全仓,含 apps/console)。三个仍在 TEST_DEBT 的包(plugin-detail / plugin-grid / plugin-form)新增 tsconfig.typetests.json 并串进 type-check,否则本 PR 的编译期钉扎不会被任何 tsc 读到——objectui#3181 / build(types): type-check this package's tests, so the spec-derivation guards actually run (objectstack#4074) #3009 那个形态;collaboration 本来一个测试文件都没有,随首个测试补上 tsconfig.test.jsoncheck:type-check-coverage 绿(22/37 compile their tests, 9 with a narrow type-assertion project)。
  • 全量测试:vitest run834 文件通过 / 1 skipped,9746 用例通过 / 25 skipped / 0 失败。十个受影响包单独复跑 185 文件 / 1805 用例全绿。新增 10 个 tripwire 测试文件、30 条断言。
  • ESLint:改动文件 0 error
  • changeset:.changeset/long-tail-spec-symbol-burn-down.md,十个包 major(公开导出改名)。

变异测试:五向报红,外加一个记录在案的洞

#变异结果
1isFileIdToken 改回逐字符相同的手抄件守卫(按名 + 按文件 file-value.ts:67);引用同一性断言(expected [Function isFileIdToken] to be [Function isFileIdToken]);而 file-value.test.ts 的 20+ 条行为断言全绿——这正是这条闸门存在的理由
2把 providers 的 Theme 还原成原来的手写联合守卫
3把派生回来的 ColumnSummaryType 改回手抄件并漏掉一个成员运行时词汇表测试指名count_unique(「spec 接受这个 summary,而渲染器没有对应的 label/聚合——授权列会通过校验然后渲染出一个空白页脚单元格」);typetests 钉扎同时
4把已烧掉的名字留在 DEBT 里(腐烂棘轮)守卫(lists 1 symbol in DEBT that no longer collide)
5tsconfig.typetests.jsontype-check 里摘掉覆盖率棘轮(objectui#3181 那条消息)

第六向是一个已知的洞,值得记在方法学里:把 ThemePreference 改回叫 Theme保留派生——守卫放行(派生就是派生),只有 tripwire 测试报红(TS2305: Module '"../types"' has no exported member 'ThemePreference' + Equal 钉扎)。这是批次 5 ConflictResolutionStrategy 那条结论的第二个活样本:「派生」不等于「派生自正确的东西」——顶着 spec 名字派生出另一个 spec 概念,守卫看不出来。所以本批每一次改名都配了钉住新名字的测试,而不是只靠守卫。

施工规约

改名一律按标识符边界替换,逐包范围内进行,没有全局替换;字符串字面量一个没动(form-section CSS 类、page:header 组件键、错误 name 等)——批次 6 DataApiValidationError 那条线上契约规则,git diff 里可核对。runnerApp 是 default export,main.tsx 用什么本地名绑定与它无关,所以这次改名的消费方成本是零。

文档随公开 API 一起改:content/docs/guide/plugin-development.mdcontent/docs/layout/page-header.mdxskills/objectui/guides/{plugin-development,architecture}.mdimport { type FieldWidgetProps } from '@object-ui/fields' 这类示例,不改就会教下一个插件作者去 import 一个不存在的名字。指向 spec 那个同名导出的地方(packages/types/src/widget.ts 的注释、.github/prompts/component.prompt.md 的 spec 示例)一律未动——它们说的是 spec 的符号,依然正确。

依赖变动:@objectstack/spec@object-ui/fields@object-ui/providersdevDependencies 移入 dependencies(前者 re-export 一个运行时函数,后者的公开 .d.ts 引用了 spec)。layout / runner / sdui-parser / collaboration 新增 spec 的 devDependency,仅供 tripwire 测试使用。与批次 5 在 @object-ui/components 上的同一处置一致——若某个包本意保持 spec-free,请指出


Generated by Claude Code

…-charts,plugin-form,providers,runner,sdui-parser)!: burn ledger batch 7 — 14 symbols stop wearing spec names
objectui#3161, objectstack#4115 ledger batch 7 — the long tail, one or two
entries per package. All ten packages leave the ledger (17 -> 3 collisions,
11 -> 1 packages; what remains is batch 8's @object-ui/types trio, untouched).
Verdicts, per SYMBOL rather than per cluster:
@object-ui/fields
- isFileIdToken -> re-export. The local copy was character-for-character
identical to the spec's function while its comment claimed to "mirror" it,
so every behaviour test passed against it and only reference identity could
tell the two apart (objectui#3003, re-proved on isAggregatedViewContainer in
objectui#3169). The regex is a wire decision: widen it server-side while a
copy here keeps the old bound and every new id reads as "not a reference".
- FieldWidgetProps -> FieldWidgetComponentProps (spec's is the DECLARED
widget-plugin props contract). The two disagree on the validation-message
key — spec `error`, objectui `errorMessage` — filed as objectui#3222; the
`[key: string]: any` that hid it is objectui#3221.
@object-ui/layout
- PageHeaderProps -> PageHeaderComponentProps, the name app-shell already
chose for the same split in objectui#3169, reused rather than re-invented.
- Page -> PageNodeRenderer. The spec's Page is the authored page
DOCUMENT; this renders types' PageNodeSchema, the SDUI NODE that
objectui#3074 had already renamed off PageSchema. Unregistered and `& any`:
objectui#3223.
@object-ui/plugin-detail
- FeedFilterMode -> re-export (a hand copy of the spec enum, in a file that
already imported FeedItemType from the same spec subpath).
- ObjectFieldLike -> ObjectDefFieldLike. The spec's is the i18n duck type
translateObject walks, and it ends in `[key: string]: any` — deriving would
trade a precise three-key layout contract for a bag (objectstack#4075 from
the spec's side).
@object-ui/plugin-grid
- ColumnSummaryConfig -> ColumnSummarySetting, derived as
NonNullable<ListColumn['summary']>. The spec owns that name for the OBJECT
form ALONE, so the local declaration wore it for a strictly WIDER type: code
written here accepted 'sum', the same name imported from the spec rejects
it. The eleven-member union beside it is the spec's ColumnSummary enum now
instead of a hand copy under a comment promising lockstep, which turns the
total Record<ColumnSummaryType, string> label map into the thing that
reports a divergence.
- isMultiValueField -> hasMultiValueShape, delegating to the spec function it
used to shadow. The two had different PRECONDITIONS (the spec's requires a
def with a `type`; this one is called with `objectFields?.[name]`), which is
worse than a plain fork: an import swapped between the modules keeps
compiling and changes behaviour only on the missing-field path.
@object-ui/collaboration
- RealtimeConfig -> RealtimeSubscriptionConfig (spec's is the app-level
realtime declaration; this is one browser socket's dial settings). Zero
shared declared keys — measured against the spec's own schema shape at
runtime, because the spec declares that schema as a PASSTHROUGH object and a
type-level key comparison against a bag answers nothing.
@object-ui/plugin-charts
- ChartConfig -> ChartContainerConfig. The one collision in this batch that
had already produced ambiguity in read code: AdvancedChartImpl declared
`config?: ChartConfig` meaning the shadcn per-series style map, lines from
comments reading "Spec `ChartConfig.yAxis`" meaning the authored document.
@object-ui/plugin-form
- FormSection -> FormSectionContainer. Deliberately NOT batch 6's ListView
exemption: ListViewProps.schema takes the spec-derived metadata as one prop,
so that renderer restates nothing, while FormSectionContainerProps takes the
same metadata FLATTENED into individual props — a rival vocabulary. `columns`
stays `1|2|3|4` rather than binding the spec key, which accepts the string
forms and normalises them in its own pipe; the pin fails if that changes.
@object-ui/providers
- Theme -> ThemePreference, derived from the spec's ThemeMode input plus the
legacy 'system' spelling. Neither of the two shapes the triage note
predicted: it was Theme['mode'] wearing Theme's name, i.e. a string union
standing where the spec has a whole document. ThemeMode, the spec's own name
for it, is a spec export too — the objectui#3169 rule again.
@object-ui/runner
- App -> RunnerApp (spec owns App for the authored application metadata AND
the App.create() builder). A default export, so no consumer churn.
@object-ui/sdui-parser
- ValidationResult -> ManifestValidationResult, per the
<what was validated>Validation<Error|Result> convention registered on
objectstack#4115; core took SchemaNodeValidationResult in batch 4.
Renames are by identifier boundary, per package, no global replace; not one
string literal changed (the `form-section` CSS class, component keys, error
names). Docs that named the renamed public types are updated with them
(content/docs, skills/objectui).
Verification: `--ledger` regenerated and set-diffed — removed is exactly these
14, added empty, @object-ui/types byte-identical. Guard green; turbo type-check
78/78 (full repo); full suite 834 files / 9746 tests passed, 0 failed; ESLint on
changed files 0 errors. plugin-detail/plugin-grid/plugin-form gained a
tsconfig.typetests.json chained off type-check (their test trees are still in
TEST_DEBT, so nothing would have compiled the new pins — objectui#3181);
collaboration gained a tsconfig.test.json with its first test file.
Five mutation directions, all red: re-forking isFileIdToken byte-for-byte
(guard by name+file, and the reference-identity assertion — while all 20+
behaviour assertions in file-value.test.ts stayed green); restoring the original
hand-written Theme (guard); re-forking the derived ColumnSummaryType with one
member dropped (the runtime spec-vocabulary test names the missing member, and
the typetests pin fails); leaving a burned name in DEBT (rotting ratchet);
unchaining a tsconfig.typetests.json (type-check coverage ratchet). A sixth
records a known hole: renaming ThemePreference back to `Theme` while KEEPING the
derivation passes the guard — derivation is derivation, batch 5's
ConflictResolutionStrategy trap — and is caught only by the tripwire test, which
is why every rename here has one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PRJtkgUAaVG11FsJQbvZWA
@vercel

vercelBot commented Aug 2, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 2, 2026 7:34pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-C51mv2gK.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.47KB3.09KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.12KB3.41KB
auth (LoginForm.js)17.86KB5.29KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.43KB2.09KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)476.12KB104.47KB
core (index.js)2.25KB0.80KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)136.23KB34.75KB
fields (index.js)223.43KB54.66KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.46KB0.96KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)5.37KB1.72KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)3.26KB1.44KB
layout (index.js)38.48KB10.67KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.05KB1.53KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.90KB12.35KB
plugin-charts (index.js)60.53KB17.12KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)111.87KB28.82KB
plugin-designer (index.js)210.51KB42.50KB
plugin-detail (index.js)230.54KB56.77KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)111.39KB26.94KB
plugin-gantt (index.js)162.26KB39.53KB
plugin-grid (index.js)185.04KB49.00KB
plugin-kanban (index.js)47.82KB13.18KB
plugin-list (index.js)104.86KB25.30KB
plugin-map (index.js)16.80KB5.24KB
plugin-markdown (index.js)13.65KB4.67KB
plugin-report (index.js)40.48KB10.57KB
plugin-timeline (index.js)25.76KB7.32KB
plugin-tree (index.js)8.34KB2.82KB
plugin-view (index.js)83.54KB20.39KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.46KB1.21KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang marked this pull request as ready for review August 2, 2026 19:40
@os-zhuang
os-zhuang added this pull request to the merge queueAug 2, 2026
Merged via the queue into main with commit 4a51e77Aug 2, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-3161-spec-symbol-batch7 branch August 2, 2026 19:41
@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

PM 验收:通过,已放行合并队列

四条门我独立核过,不是采信 PR 描述:

1. 台账 set-diffgit diffscripts/check-spec-symbol-derivation.mjs:0 added / 34 removed,且只动 DEBT 块。剩余恰为 @object-ui/types 三条(JoinedReportBlock / NavigationItem / NavigationItemSchema),即批次 8(#3162,等 objectstack#4171)的全部内容,逐字节未动。

2. 一条新 ALLOW 都没加。 14 个符号全部靠 re-export / 派生 / 委托 / 改名真解决。这比门槛要求的强——批次 6 的 ListView / UserFilters 是走 ALLOW 的,那是记账不是还债。本批零新增记账。

3. 线上契约未被改名波及(批次 6 DataApiValidationError 那条规则)。对全 diff 扫描 'page' / 'page:header' / 'form-section' 一类字面量,只有两处命中,都在正确方向上:一句注释,和一条钉住PageNodeSchema['type'] === 'page' 的类型断言。没有任何注册键、CSS 类名、错误 name 被改。

4. CI — 15 项 13 绿 2 skipped,零红;mergeable_state: clean

顺带裁一下 open question A(spec 依赖边):按 A,接受

@objectstack/specfields / providers 从 devDep 移入 dep,layout / runner / sdui-parser / collaboration 新增 devDep——接受现状,理由是选项 B 的字面含义就是「把这批刚拆掉的 fork 再抄回去」:

  • 一个包的公开 .d.ts 引用了 spec,却把 spec 放在 devDependencies,消费方拿到的是解析不了的类型。这不是洁癖问题,是坏掉的包;
  • 声明依赖的替代品是复制,而复制正是这整个燃尽任务要消灭的缺陷;
  • 选项 C(经 @object-ui/types 转一道)保住了绑定,但让来源多一跳、更难读——与「让 AI 写元数据不容易出错」正好相反。

与批次 5 在 @object-ui/components已合并的同一处置一致,所以不是新先例。版本一律 ^17.0.0-rc.1,与仓内其余 pin 一致。

若某个包是按策略刻意保持 spec-free,在 objectstack#4115 指出具体是哪个,我改派回滚那一个——package.json 级改动,回退成本接近零,不值得为它挡住这批。

本批捎带的三个发现已分别处置


Generated by Claude Code

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

台账燃尽批次 7/8 · 单发长尾 9 包,14 符号(objectstack#4115)

2 participants

@os-zhuang@claude