Skip to content

fix(app-shell): a published configSchema can no longer delete a node's sibling-block editors (objectstack#4045) - #3082

Merged
os-zhuang merged 2 commits into
mainfrom
claude/amazing-darwin-2z3fab
Jul 31, 2026
Merged

fix(app-shell): a published configSchema can no longer delete a node's sibling-block editors (objectstack#4045)#3082
os-zhuang merged 2 commits into
mainfrom
claude/amazing-darwin-2z3fab

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

objectstack#4045 的跨仓库收尾 —— 堵掉 connector_action 事故的根因,而不只是那一个实例。

问题

FlowNodeInspector 这样解析表单字段:

const resolved = serverFields ?? fieldsForNodeType(node?.type);

发布了 configSchema整表替换手写字段组。但一份 configSchema 按契约只描述 node.config(ADR-0018),而 jsonSchemaToFlowFields 产出的每个字段都根在 ['config', key] —— 所以这个替换会把根在别处的编辑器全部静默删除

清点下来共 18 个字段在爆炸半径内:

字段数 节点
connectorConfig.* 3 connector_action
waitEventConfig.* 5 wait
boundaryConfig.* 6 boundary_event
顶层 timeoutMs 4 http_request / script / subflow / connector_action

waitboundary_event 而言,那个兄弟块就是它们的全部契约 —— 一旦有人给它们发布 schema,属性面板会整个空掉。

这不是假设 —— connector_action 已经这样丢过一次

它的描述符发布了一份把 connectorId/actionId/input 声明成 config 键的 schema。连着真实后端时,生成的表单替换掉 connectorConfig 那组(连 connector / action 选择器一起),于是在 Studio 里配置 connector 节点的作者把三元组写进了 node.config —— 执行器永远不读那里,节点必然以 connectorConfig.connectorId and .actionId are required 拒绝执行。

objectstack#4210 已在服务端撤掉那份 schema。本 PR 是防止下一份错位的 schemawaitboundary_event 做同样的事。

改法

新增 mergeServerFlowFields(),按「根」拆分归属:

  • 服务端拥有 config 根的字段 —— 它才是「执行器读什么」的权威,所以它的集合替换手写的 config 字段而不是与之合并(陈旧的客户端键不该残留);
  • 非 config 根的字段始终保留自手写表,保持声明顺序;
  • 服务端若发来与被保留的兄弟键重名的字段,直接丢弃而不是渲染两遍 —— 「两个编辑器编辑同一个值、其中一个写到没人读的地方」是同一个 bug 换张皮。

没有发布 schema 时,手写组照旧整组使用,行为不变。

验证

做了变异验证,因为「通过」本身不算证据:把实现退回旧的 return serverFields 整表替换后,3 条新断言全部转红(其中一条是 connector_action 事故的原样复现);恢复实现后 71 通过。

  • 相关三个套件:71 通过
  • 全仓 pnpm test8677 通过 / 0 失败(749 文件)
  • pnpm type-check:通过

⚠️ 一个过程记录:第一次跑测试时我用了 pnpm --filter @object-ui/app-shell test -- flow-node-config,报 107 通过 —— 那是假绿。vitest 配置是仓库根作用域的(include: packages/**),从包目录跑包相对路径匹配不到任何文件。正确姿势是在仓库根用仓库相对路径。

🤖 Generated with Claude Code

https://claude.ai/code/session_01UDhMtxPLLoFaGtdpNA7xTU


Generated by Claude Code

claude added 2 commits July 31, 2026 00:57
…s sibling-block editors (framework#4045)

The inspector resolved its fields as `serverFields ?? fieldsForNodeType(type)` —
an engine-published configSchema REPLACED the hand-written group wholesale. But
a configSchema describes `node.config` and nothing else (ADR-0018), and
`jsonSchemaToFlowFields` roots every field it emits at `['config', key]`, so the
replacement silently deleted every editor rooted anywhere else.

18 fields are exposed to that: connectorConfig.* (3), waitEventConfig.* (5),
boundaryConfig.* (6) and the top-level timeoutMs (4). For `wait` and
`boundary_event` that is their ENTIRE contract.

Not hypothetical — connector_action already lost this way. Its descriptor
published a schema declaring connectorId/actionId/input as CONFIG keys, so
against a live backend the generated form replaced the connectorConfig group,
pickers included, and an author configuring a connector node in Studio wrote the
trio where the executor never reads. The node then refused to dispatch with
"connectorConfig.connectorId and .actionId are required". framework#4210 retired
that schema; this stops the next mis-rooted one from doing the same.

mergeServerFlowFields() splits the resolution by root: the server owns the
config-rooted fields (it is the authority on what the executor reads, so a stale
client key must not linger), and the hand-written non-config fields are always
preserved in declared order. A server field duplicating a preserved sibling key
is dropped rather than rendered twice — two editors for one value, one writing
where nothing reads, is the same bug in a different hat.

Verified by mutation: reverting to the old replacement turns all three new
assertions red, including a replay of the connector_action incident.
@vercel

vercel Bot commented Jul 31, 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)
objectui Ignored Ignored Jul 31, 2026 12:59am

Request Review

@github-actions github-actions Bot added the tests label Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 27.9 KB 350 KB
Entry file index-Dgfz9n2J.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.26KB 2.99KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 471.25KB 102.82KB
core (index.js) 2.16KB 0.78KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.10KB 34.56KB
fields (index.js) 222.07KB 54.35KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 25.17KB 5.80KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 38.45KB 10.67KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.76KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (retry.js) 3.48KB 1.61KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.90KB 12.35KB
plugin-charts (index.js) 60.52KB 17.11KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 111.59KB 28.74KB
plugin-designer (index.js) 210.51KB 42.50KB
plugin-detail (index.js) 221.81KB 54.28KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 110.71KB 26.67KB
plugin-gantt (index.js) 162.26KB 39.53KB
plugin-grid (index.js) 182.21KB 48.24KB
plugin-kanban (index.js) 47.82KB 13.18KB
plugin-list (index.js) 103.85KB 24.80KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 40.32KB 10.53KB
plugin-timeline (index.js) 25.75KB 7.32KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 85.79KB 20.99KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 3.47KB 1.54KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 2.07KB 0.99KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 1.08KB 0.64KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.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 July 31, 2026 01:12
@os-zhuang
os-zhuang merged commit d132bb5 into main Jul 31, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/amazing-darwin-2z3fab branch July 31, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants