Skip to content

fix(app-shell): Studio's flow start node stops offering 「Platform event」, a trigger no engine routes (objectui#11067) - #11090

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11067-drop-platform-event-trigger
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11067-drop-platform-event-trigger

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11067
Clause-②: no

Studio's flow start-node inspector offered a 「Platform event」 trigger that wrote triggerType: 'event'. The spec's resolveFlowTriggerKind answers no trigger kind for that token, and no engine trigger binds it, so a flow authored with it never fired and nothing said so. Implemented along triage's direction (comment 5887202725): remove the option, don't invent the route.

What changed

  • The option is gone. The start inspector's Trigger select no longer offers 「Platform event」. A comment beside the objectui#11054 api note says why.
  • The two start-node gates stop naming event. The Object and Entry condition fields no longer appear for that token alone. A value already stored on such a node stays on screen under the existing stored-value re-show rule, flagged as retained.
  • A stored event is not rewritten. Nothing new was built for it. The select already shows a stored value it does not offer as a flagged, selectable row: InspectorSelectField owns that rule (objectui#8488), and the flow select branch words the flag through flagUnknownValue with the catalogue key engine.form.deprecated. A save keeps the value as stored.
  • The zh overlay drops the option's label (FLOW_FIELD_ZH, the start node's triggerType options). See the next section.

Beyond the claim's file surface: one line in metadata-admin/i18n.ts

The claim names flow-node-config.ts. The option's zh label lives in the zh overlay in metadata-admin/i18n.ts, and localizeField reads an overlay label only through an offered option. Once the option leaves, its label is read by nothing. The one-line removal is the zh half of the same option declaration. It follows the precedent of PR objectui#11064, which swapped the overlay's webhook key for api in the same change. It meets the four conditions for an in-place fix:

  1. It is the same defect class.
  2. It is mechanical.
  3. The in-flight PR objectui#11084 does not touch the file. Its REST file list has 10 files, none of them i18n.ts.
  4. It adds no verification surface beyond this PR's app-shell tests and type-check.

A pin states the rule rather than the one token: every zh overlay key for the trigger select names an offered option. Ablation A3 below shows that pin going red when the label is put back.

Measured before building

  • The resolver, probed with installed @objectstack/spec 17.4.0. triggerType: 'event' gives undefined. So does event with an objectName. The control api gives api. objectstack origin/main b80ab579 has the same resolveFlowTriggerKind, and FLOW_TRIGGER_KINDS there is record_change, time_relative, schedule and api.
  • The flag exists, so there is no new UI element and no new string. No locale-pack key was added, so the pack serialisation behind objectui#11056 does not apply. That card has also since merged as 873284657, this branch's base.
  • Census of stored triggerType: 'event': zero. The pattern allows an optional quote around the key, then a colon, then the token quoted or bare.
    • objectui 873284657, all tracked files: event 0. Lit controls on the same shape: api 21 hits in 4 files, record- 13 hits in 8 files.
    • objectstack b80ab579, examples/: event 0. Controls: api 1 hit (the showcase inbound flow), record- 22 hits in 5 files.
    • objectstack b80ab579, whole tree: event 1 hit. It is not the token: triggerType: event is a JS variable in trigger-record-change's bulk-write-per-row-context.test.ts.
    • The wording 「Platform event」 appears nowhere else in objectui (case-insensitive, all tracked files).
  • Nothing publishes a start node configSchema that could put the option back. objectstack service-automation's builtin node forms carry no start node, and the inspector's field table for start is this repo's own.

Pins

  • flow-node-config.platformEvent-11067.test.ts (table half):
    • the resolver answers no kind for event, with an api control;
    • no option writes event, and no start field is gated on it;
    • every value a start field is gated on is one the select offers;
    • the zh overlay labels only offered options;
    • a stored event no longer shows the Object and Entry condition fields on the token alone, and a stored objectName beside it stays visible, flagged controller-off, with a live record-trigger control.
  • FlowNodeInspector.platformEvent-11067.test.tsx (what the author sees):
    • the open list has no such row, with a Webhook / API control;
    • in en-US and zh-CN, a stored event draws the value under the flag. The expected text is built with the same flagUnknownValue and catalogue key, so the wording is not pinned here. unknownValueFlag.i18n-9652.test.tsx owns the wording.
    • the flagged row is selectable;
    • an edit that does not touch the select saves { triggerType: 'event' } and { triggerType: 'event', objectName: 'task' } back unchanged.

Ablations

Run at c5ebc3d93 (the fix and pins committed). Each leg went through objectstack's scripts/ablation-replace.mjs. That tool checks that the anchor hit exactly once, that the blob changed, and that the restore proof holds: blob equal to HEAD and git diff HEAD empty. Each red/green split was predicted before the run.

Leg Mutation Predicted Observed
A1 the option row put back 5 red, 8 green Tests 5 failed | 8 passed (13)
A2 'event' put back on the Object gate 4 red, 9 green Tests 4 failed | 9 passed (13)
A3 the zh overlay label put back 1 red, 12 green Tests 1 failed | 12 passed (13)

The A3 mutant's blob is 98a9c7936. That is the base file's own blob, so the leg restored the old line byte for byte. The two round-trip cases stay green in every leg, which was predicted: no leg moves the save path. They guard a future rewrite of a stored value, and none of the three mutations exercises that.

Gates

The final head is 4de4b51ea. It differs from c5ebc3d93 by one test-only line: readOnly={false} on the new inspector pin's mount.

  • Build. turbo run build --filter='@object-ui/app-shell^...' --concurrency=2: Tasks: 28 successful, 28 total, run at c5ebc3d93. The dependency closure has no diff.
  • Type-check at 4de4b51ea. pnpm --filter @object-ui/app-shell type-check exits 0. It runs tsc --noEmit, then tsc -p tsconfig.test.json, whose include is src/**/*.test.ts and src/**/*.test.tsx. At c5ebc3d93 the test project refused the new inspector pin's mount for a missing required readOnly prop. That red is how we know the test project reads the new file. 4de4b51ea is that repair.
  • Tests at c5ebc3d93. vitest run packages/app-shell/src/views/metadata-admin/inspectors/: Test Files 111 passed (111) and Tests 1554 passed | 1 skipped (1555).
  • Tests at 4de4b51ea. The two new pin files plus the two objectui#11054 pin files: Test Files 4 passed (4) and Tests 42 passed (42).
  • Other gates at 4de4b51ea, each exit 0:
    • node scripts/check-changeset-presence.mjs;
    • node scripts/check-control-bytes.mjs;
    • check:new-line-citations, which reports 0 new;
    • check:vi-mock-specifiers, check:vi-mock-inherit and check:vi-mock-override-shape;
    • check:test-path-roots;
    • check:i18n-designer-parity and check:i18n-drift;
    • check:designer-field-key-parity. It is run anyway, although no field key moved.

A declared narrowing: the full vitest farm and the repo-wide pnpm lint belong to CI.

  • ESLint read the 4 changed .ts / .tsx files. That count comes from --format json: 4 results, 0 errors, 0 warnings.
  • eslint.config.js sets no projectService and no parserOptions.project, so type-aware linting is off. This diff therefore cannot move a lint verdict on a file it does not touch.

Acceptance notes

  • FLOW_META_ZH.type (the canvas header pill for the flow-level type) still carries event: '平台事件', and also webhook, manual and time_relative. It is a display fallback for a stored flow-level value, not an option anyone picks, and is outside this card. The installed spec's flow type enum is autolaunched, record_change, schedule, screen and api. Carrier: none.
  • This is a read, not a measurement: objectstack's validate-flow-trigger-readiness reports an off-grammar triggerType only on a type: 'record_change' flow. Whether os validate says anything about triggerType: 'event' on an autolaunched flow was not run. After this PR, the census finds no producer of the token. Carrier: none.

Generated by Claude Code

…latform event

`triggerType: 'event'` is a token `resolveFlowTriggerKind` answers no kind
for and no engine trigger binds, so a flow authored with it never fired.
The option leaves the select, the two start-node `showWhen` lists stop
naming it, and the zh overlay drops the option's label. A stored `event`
is not rewritten: the select shows it as its flagged unknown value.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
… event

The table half asks the spec resolver that `event` is unrouted, and pins
that no option writes it, no start field is gated on it, and the zh
overlay labels only offered options. The inspector half pins that a
stored `event` shows as the select's flagged unknown value and is saved
back as stored.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
… pin

`MetadataInspectorProps` requires it; the test project's type-check
refused the mount without it.

Claude-Session: https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 5 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6310-designer-formula-key-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: designer.field.formula ('Formula (CEL)') in packages/app-shell/src/views/metadata-admin/i18n.ts, a different and live key belonging to metadata-admin's ObjectFieldInspector — the surface that still authors formula expressions.

.changeset/6499-inactive-values-retained.md

  • names flow-node-config.ts → packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts — edited by this change

    • New inactiveRetainedKind(field, node, fields) in flow-node-config.ts — a pure read that reports a field rendered ONLY because the stored-value re-show rule fired. It distinguishes 'controller-off' (a real toggle the author can switch back on) from 'no-controller' (the __legacy__ render-only keys, where no such toggle exists and saying otherwise would be a fresh lie). - FlowNodeConfigField renders the notice beside the affected control, with a "Clear value" action so the author can discard the residue deliberately. Read-only inspectors show the notice without the action. - Coverage is every showWhen group in the inspectors, pinned mechanically: all 33 gated fields across the descriptor tables, plus the two runtime producers (an engine-published configSchema and a connector input schema) that mint groups no source file contains.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names packages/app-shell/src/views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/8632-malformed-picklist-option-loud.md

  • names views/metadata-admin/i18n.ts → packages/app-shell/src/views/metadata-admin/i18n.ts — edited by this change

    Two new strings land in the designer's own en / zh tables — the metadata-admin console owns its strings in views/metadata-admin/i18n.ts and is deliberately outside the ten locale packs (packages/i18n/README.md, "Scope — the engine.* carve-out").

.changeset/9278-end-outcome-select.md

  • names packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts → packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts — edited by this change

    packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts declared the end group's outcome key as a free-text box with placeholder: 'success · failure'. EndConfigSchema.outcome is a closed enum of completed | refused defaulting to completed, and FlowNodeSchema discriminates an end node's config through it — so both printed words are refused at parse, not ignored at run time. Measured against the installed @objectstack/spec (17.4.0), with the accepted row in the same output so the refusals are a reading rather than a dead probe:

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with 3e71a9825 (merge-base with origin/main): 4 file(s) changed outside .changeset/, read against 1745 pending declaration(s) that publish a body (2349 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3108.6 KB 3149.4 KB
Main entry chunk (gzip) 149.3 KB 350 KB
Entry file index-CxzLJzgo.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
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) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
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) 558.78KB 133.92KB
core (index.js) 9.94KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 227.99KB 63.22KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
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.75KB
mobile (index.js) 1.99KB 0.87KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.58KB 4.90KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.25KB 2.17KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 137.68KB 36.71KB
plugin-designer (index.js) 215.78KB 44.42KB
plugin-detail (index.js) 233.54KB 61.80KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 169.60KB 43.32KB
plugin-gantt (index.js) 171.88KB 42.61KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 31.07KB 9.15KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 90.43KB 22.76KB
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.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 8.61KB 3.45KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.17KB 2.73KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 22.61KB 7.40KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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 (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.30KB 6.99KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4de4b51ea4a4b3e5f16ceb3855d3719dde8d244f
Local-runs: none

Inputs: card #11067 (body and its 3 comments: triage 5887202725, claim 5888477811, os-dev-report 5889800745); PR #11090 (body, 5-file list, net diff against main, +299/-4); the head's check-runs read with filter=latest (42 runs at the first read, 43 once the Test rollup appeared). Context reads by the contents API at the head: flow-node-config.ts, i18n.ts, inspectors/_shared.tsx, FlowNodeConfigField.tsx, app-shell index.ts and package.json; 47 flow/trigger/locale sources under packages/app-shell/src and apps/console/src grepped for the token and the wording; the six other open PRs' file lists and a compare against main of the recent PR-less claude/issue-* branches. Nothing built, run or re-run.

① Derived judgments

Binding direction, triage 5887202725: remove the option, don't invent the route; a stored triggerType: 'event' loads as the select's flagged unknown value and is never silently rewritten; pins: the option is absent, a stored event start node shows the flag and saves unchanged.

  1. The option leaves the select — right. flow-node-config.ts drops { value: 'event', label: 'Platform event' } from the start inspector's triggerType options (:412 at the head is now the comment saying why, beside the objectui#11054 api note). No other offered option or option label among the 47 candidate sources carries the token or the wording. The only other 平台事件 at the head is FLOW_META_ZH.type.event (i18n.ts :6245), the flow-level type header pill's display fallback through translateFlowMeta, which offers nothing to pick (see ③).
  2. Both showWhen gates drop 'event' — right, and that is the whole of the card's named site (the select and the objectName / condition lists, now :437 and :448). No third list names the token at the head: the file's remaining event hits are the waitEventConfig / boundary_event field paths and the help text "every event".
  3. A stored event loads as the flagged unknown value and is never rewritten — right, by construction rather than by new code. InspectorSelectField (_shared.tsx :445-:530, objectui#8488 / finding(app-shell/inspectors): a FAILED roster fetch flags a valid stored value as (not in object) PERMANENTLY - the load-failure arm of the blindness objectui#8862 closed for the pending arm #9651) synthesises a selectable first row for a non-empty value the roster does not offer; a literal options array makes rosterVerdict answer answered (:274), so the row draws unknownValueLabel(current), which the flow select branch (FlowNodeConfigField.tsx :384-:391) words as flagUnknownValue(v, t('engine.form.deprecated')). Nothing on that path commits: onValueChange fires only on an author pick. The diff adds no write, no migration and no default, and the dev built no new UI or string, which is the right reading of the claim's "measure first, build only if missing". The accept set for the stored value does not move: event stays a value a node may carry; only the offer set narrows.
  4. Gated fields on a stored-event node — right. objectName / condition no longer show on the token alone; a stored objectName beside it stays visible through the stored-value re-show rule (isFieldVisible :1355, objectui#6499 Option C) and reads inactiveRetainedKind as controller-off (:1467), so retained config is neither hidden nor pruned. Pinned by the table half's last two cases with a live record-after-create control.
  5. Public surface: nothing moves. flow-node-config.ts and the inspector are not re-exported by packages/app-shell/src/index.ts (no export *, no inspectors/ path), and package.json exports only . and ./styles.css. The narrowing is the Studio select's offer set, a UI behaviour, not a published type, export or accept set.
  6. Pins match the direction. flow-node-config.platformEvent-11067.test.ts: the resolver answers no kind for event with an api control; no option writes event; no gate names it; every gated value is offered; the zh overlay labels only offered options; a stored event hides the two fields on the token alone and keeps a stored objectName flagged retained. FlowNodeInspector.platformEvent-11067.test.tsx: no such row with a Webhook / API control; en-US and zh-CN draw the flagged value with a not-bare control; the flagged row is selectable; two round-trips save { triggerType: 'event' } and { triggerType: 'event', objectName: 'task' } back unchanged. Ablations A1-A3 in the PR body match their predictions; the round-trip cases are unablated by design and the dev says so.
  7. Check-runs on the head (filter=latest): 43 runs, all completed, 40 success, 3 skipped, 0 failure. The 8 test shards, the Test rollup, Test (dist pins), Type Check, Lint, Spec Main Shape Gate, Build & E2E, Build Docs, Bundle Analysis, the four Changeset runs, Inert vi.mock Specifier Check, Line Citation Gate, Control Byte Scan, Governed Surface Queue Guard and the doc, skill and import-graph checks are all success. The 3 skipped are Test (coverage), its shard matrix and dependabot, skipped by workflow condition, not by a failure. The test shards and Spec Main Shape Gate were still in progress when this review started (12:02Z) and were waited on in the foreground until the last completed at 12:09:50Z; no run was re-run.

② Semver level

.changeset/11067-drop-platform-event-trigger.md declares '@object-ui/app-shell': patch — right. The diff publishes a fix in one released package: a Studio select stops offering a value no engine routes, and a stored value is kept as written. No type, export, schema or accept set changes (①.5). Changeset Bump Policy, Changeset Declaration, Changeset Claim Re-read and Changeset Fixed Group Check are green on the head. The changeset body says what an author sees and what to do (pick an offered trigger).

Clause-②: no — matches the claim 5888477811 and the PR body. Nothing here moves an objectstack contract, a spec accept set or a public export, and the direction forbids an event trigger kind.

③ Boundary flags

Dev flags: deviations 4, open_questions 0, out_of_scope_findings 2 (os-dev-report 5889800745).

  • D1, surface deviation: one line removed from packages/app-shell/src/views/metadata-admin/i18n.ts (FLOW_FIELD_ZH start.triggerType.opts.event: '平台事件'), a file the claim does not name — answered, accepted. (a) It is the same option's zh half, inside the direction: localizeField (flow-node-config.ts :1278-:1280) applies the overlay only by mapping field.options and reading z.opts[o.value], so an overlay key is read exactly when the select offers that value; "the option leaves the select" takes its zh label with it. (b) Leaving it would have left a dead overlay row: with the option gone nothing reads opts.event, and the pin "the zh overlay labels only options the select offers" (ablation A3, 1 red / 12 green as predicted, mutant blob 98a9c7936 equal to the base blob) would have failed or been written around. Same shape as PR objectui#11064's webhook to api overlay swap. The seat may amend the claim's file surface to name i18n.ts.
    Landing-order note for the seat: no open PR touches metadata-admin/i18n.ts (file lists of fix(plugin-form): object-form's modalCloseButton: false hides the modal's close button (objectui#11061) #11091, feat(core,fields): a number field's authored useGrouping decides its thousands separator, relayed to every cell (objectui#11026) #11089, chore(deps): resolve @objectstack/* 17.5.0, and the zod 4.6.5 it requires, in pnpm-lock.yaml (objectui#11073) #11086, feat(cli): objectui validate and objectui check judge through the strict authoring face (objectui#5250, slice A) #11069, feat(plugin-grid): one display page size read from the spec, and a distinct fetch batch (objectui#9853) #10278 and chore: release packages #5400 read). Of the PR-less in-flight branches compared against main, claude/issue-11027-preview-display-keys (335a6c38, ahead 3, no PR yet) adds one engine.appPreview.areas row to ENGINE_STRINGS_EN at :1465 and to ENGINE_STRINGS_ZH at :4352, about 1,500 lines above the FLOW_FIELD_ZH start.triggerType overlay at :5886-:5902 this PR edits: no textual overlap, either landing order merges clean. The stale claude/issue-10835-dashboard-preview-debug-run-locale (behind 146) adds ENGINE_STRINGS rows at :418 / :1218 / :2807 / :3619, also disjoint. claude/issue-10993-batch2-i18nlabel-reads does not touch the file.
  • D2, the full inspectors-directory vitest run was at c5ebc3d93, not the head — answered. The head differs by one test-only readOnly={false} line; the head's CI test shards 1-8 are the run of record (①.7), and the type-check and the four flow pin files were re-run at the head.
  • D3, verify-lock queue timeouts and a lowered wait — process only. No bearing on the contract; nothing to escalate.
  • D4, commit trailers follow objectui AGENTS.md's model-free pair — process only. The role file's precedence is the dev's to follow; not a contract gate here.
  • out_of_scope_findings, both carrier none, noted not filed: (i) FLOW_META_ZH.type keeps event, webhook, manual and time_relative display labels for the flow-level type header pill (i18n.ts :6235-:6260, verified at the head): a fallback for a stored value, not an offered option, outside this card; (ii) objectstack's validate-flow-trigger-readiness reports an off-grammar triggerType only on record_change flows, a read not a measurement, and the census finds no producer of the token after this PR. Neither blocks; both are the seat's to file or drop.

Implemented-by: claude/issue-11067-drop-platform-event-trigger
Reviewed-by: session_011p7ikEivgXefNDaE5S5Uec

VERDICT: PASS


Generated by Claude Code

Sign up for free to 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.

finding(app-shell): Studio's flow start-node 「Platform event」 option writes a trigger kind the engine never routes, so the flow never fires

2 participants