Skip to content

fix(app-shell): packaged flow rows keep their height; the platform reason opens from a neutral chip (objectui#12609) - #12647

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-12609-packaged-automation-rows
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-12609-packaged-automation-rows

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #12609

Clause-②: yes

Setup › Packaged automation: a flow row that carries the runtime's reason sentence no longer grows to several lines. The sentence opens, verbatim and in full, from a neutral Details chip on the flow name's line. This is the card as triage narrowed it (comment 6210990723): target 3 only, the half that classifies nothing.

What changed

  • packages/app-shell/src/views/setup/PackagedAutomationPage.tsx
    • The paragraph element that rendered row.reason inside the flow cell is gone.
    • A new local component, ReasonDetails, draws a small outline chip (an Info icon and the word "Details") inline after the flow's label. The chip opens a popover that shows the sentence whole and muted, with no alert role.
    • The chip renders only when the row carries a non-empty string reason. That rule is the existing one in joinPackagedFlows, unchanged. The chip's label, icon and classes are the same on every row, and nothing reads, matches or compares the sentence.
    • A popover, not a tooltip: the sentence runs to several lines and can name an operator switch the admin needs to copy. A popover opens on click, tap and keyboard, and stays open to be read and selected.
    • Rows without a reason render the same markup as before.
  • packages/i18n/src/locales/*.ts: one new key, packagedAutomation.reasonDetails, in all ten packs. Each pack uses the word it already uses for "Details" elsewhere.
  • .changeset/12609-packaged-flow-reason-chip.md: a patch for @object-ui/app-shell and @object-ui/i18n, with the Clause-② paragraph (one pack key added; no export or prop changes).

Governing text

@objectstack/spec FlowRuntimeState.reason, verbatim: "A free-form sentence, deliberately — the shape the two surfaces that already carry this reason use. Consumers RENDER it; ⛔ do not parse it." This PR adds no regex, includes or match on the reason, and the chip's wording names no cause.

Tests

PackagedAutomationPage.test.tsx, section 6 (the platform reason for an unbound flow), reworked:

  • A deployment-policy row and a binding-failure row. The row holds no paragraph element and none of the sentence. Its "Details" chip opens a dialog-role disclosure whose text is exactly the sentence: muted, with no alert.
  • The chip does not classify. The policy row's chip and the binding-failure row's chip have the same text ("Details") and the same classes.
  • Rows without a reason have no chip and the same text as before. This covers a bound row, a disabled row, an older backend's bare row and a non-string reason.

pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/setup/PackagedAutomationPage.test.tsx: 15 passed (15).

Ablation (each red proven, then restored)

Each mutation went through objectstack's scripts/ablation-replace.mjs in wrap mode. The anchor must hit, and the tool checks on-disk counts and blob hashes. Restore is proven by blob == HEAD and an empty git diff HEAD. The test imports the page from src/, so no build sits between the mutation and the run.

Mutation Red tests
A: the reason paragraph put back in the row, chip kept policy row, binding-failure row: 2 failed, 13 passed (the row's paragraph count was 1, not 0)
B: the chip removed policy row, binding-failure row, does-not-classify: 3 failed, 12 passed
C: the chip drawn on every row no-reason rows, non-string reason: 2 failed, 13 passed
D: the chip's label chosen from the sentence's words policy row, does-not-classify: 2 failed, 13 passed

Mutation A's first attempt was refused by the tool before anything ran. Its replacement contained the anchor, so the anchor count could not drop. It was re-anchored and re-run, and the counts above come from the run whose mutation landed.

Local gates, all on ad380ce

objectui is hand-derived from its own package.json and .github/workflows/ci.yml. Each exit code was captured before any pipe. CI runs the full farm.

  • pnpm exec turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 (upstream closure of app-shell, which contains i18n's): exit 0, 28 tasks successful (28)
  • pnpm --filter @object-ui/i18n type-check and pnpm --filter @object-ui/app-shell type-check: exit 0. app-shell's tsconfig.test.json includes src/**/*.test.tsx, so the test file is compiled.
  • pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/setup/ packages/i18n/: exit 0, 88 files passed, 1387 tests passed, 13 skipped
  • pnpm check:i18n-keys, check:i18n-drift (1 key added, 0 values changed), check:i18n-dead-keys (the new key is not listed): exit 0
  • pnpm check:changeset-claims, pnpm changeset:check, node scripts/check-changeset-presence.mjs: exit 0
  • pnpm check:test-path-roots, pnpm check:spec-symbols, pnpm check:new-line-citations (0 new): exit 0
  • pnpm exec eslint --no-inline-config on the 12 changed .ts and .tsx files: exit 0, no output

Acceptance notes

  • The switch is unchanged. It still shows the row's enabled field, so a flow that deployment policy keeps unarmed still reads On. Showing the effective state needs the structured discriminator that spec(automation): FlowRuntimeState gains a structured discriminator beside reason (deployment policy vs a binding failure), so a consumer can classify a disabled packaged flow without parsing the sentence (objectui#12609) objectstack#23842 adds, and this PR does not touch the switch.
  • Card targets 1 and 2 are not here. The chip that names the deployment policy and the once-banner both need that discriminator. Per triage, the console half of those is filed once the field is in a published @objectstack/spec.
  • Acceptance 2, the screenshot at 1280 px in light and dark: NOT MEASURED in this run. A reader needs four things:
    • a backend with packaged time-triggered flows and OS_AUTOMATION_SCHEDULED_WORK_ENABLED unset, so those rows carry the policy sentence;
    • a console started from this branch, on its own port, pointed at that backend;
    • the page /apps/com.objectstack.setup/component/automation/packaged;
    • a click on a row's Details chip, to read the popover.
  • Row height in pixels: NOT MEASURED. No browser ran here. What the tests pin is structural: the row holds no paragraph, and the chip sits inline in the label's line (leading-4 text, a one-pixel border, no vertical padding). Rows without a reason were already two lines tall (the label above the machine name), in cells with the table primitive's p-4 padding. So the claim this PR makes is "a reason row is as tall as any other row", not a pixel figure; the screenshot reading above should take the figure.
  • Card acceptance 3, pnpm --filter @object-ui/app-shell test: the whole-package run is left to CI. Locally, the setup directory and the whole i18n package ran, as listed above.
  • Docs: no page under content/docs/ describes how this page shows the reason, so no doc changed.
  • Overlap: objectui#12606 (another seat, in flight) owns the header row of Setup component pages. This PR touches only this file's flow rows and its module docblock, and the later lander merges main.

Generated by Claude Code

…ason opens from a neutral chip (objectui#12609)

The runtime's `reason` sentence for an enabled-but-unarmed flow rendered as
a paragraph inside the Setup › Packaged automation table row, growing each
such row to several lines. It now opens, verbatim and muted, from a popover
behind a "Details" chip on the flow name's line. The chip's label and look
follow only whether a reason was sent, never what it says: the contract
renders the field and never parses it, so nothing here names a cause.

One i18n key, `packagedAutomation.reasonDetails`, in all ten packs.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 70 chunks) 3095.7 KB 3119.4 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-BbcAfYxK.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) 21.19KB 7.76KB
app-shell (runtime-config.js) 22.59KB 7.89KB
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) 20.39KB 6.02KB
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.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 41.19KB 11.12KB
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.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 598.32KB 144.25KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 248.20KB 69.22KB
fields (index.js) 283.17KB 72.17KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.67KB 11.85KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
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.04KB 3.92KB
plugin-calendar (index.js) 54.94KB 16.05KB
plugin-charts (index.js) 84.97KB 23.36KB
plugin-chatbot (index.js) 201.89KB 48.13KB
plugin-dashboard (index.js) 146.15KB 39.55KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 262.91KB 69.49KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 180.92KB 46.73KB
plugin-gantt (index.js) 178.36KB 45.08KB
plugin-grid (index.js) 265.13KB 73.29KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 129.90KB 33.12KB
plugin-map (index.js) 27.07KB 8.96KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 93.43KB 23.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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: TIER_DEFAULT
Head-sha: ad380ce46dbbc2512e6b0fe9d651d461324075e8
Local-runs: none

Face: the Clause-②: yes declaration on claim 6212704910 (objectui#12609, the half triage narrowed at 6210990723). It was declared on «拿不准 ⇒ 按 yes», because a neutral chip might need a pack key. The committed tree has no published schema and no governed path: check-governed-merges --pr reports NOT governed, +166/−41 over 13 files. Inputs: the diff, the card and its narrowing, dev report 6213026539, and this head's check-runs (33 success, 3 skipped by design).

① Derived judgments

  • @object-ui/i18n. One key is added, packagedAutomation.reasonDetails, in all ten packs. No key is removed or renamed, and no value changes. The exported en pack's type is its literal content, so the key set widens by one. A widening, as the changeset's Clause-② paragraph declares. Correct.
  • @object-ui/app-shell. ReasonDetails is a module-local function in PackagedAutomationPage.tsx and is not exported. The page's props and exports are unchanged. Its imports (Popover, PopoverContent, PopoverTrigger, lucide Info) are existing dependencies. No public-surface change. Correct.
  • The spec contract. FlowRuntimeState.reason is "RENDER it; ⛔ do not parse it". The chip renders when joinPackagedFlows kept a non-empty string, a rule this PR leaves unchanged. Its label, icon and classes are constant. The popover shows {reason} verbatim, and no regex, includes or comparison reads it. The does-not-classify test pins this, and ablation D (a label picked from the sentence) turns it red. Correct.

② Semver level

@object-ui/app-shell patch and @object-ui/i18n patch. Strict semver would read the added key as minor. The repo's own practice ships an added pack key as an i18n patch: .changeset/12528-list-dashboard-chrome.md on main (646caea) adds sortRelationalPicked under '@object-ui/i18n': patch. Every package is in the fixed group in pre mode (next), so the group's release level is set by the pending changesets as a whole, and this file cannot lower it. Changeset Bump Policy and Changeset Fixed Group Check pass on this head, and the changeset names the widening in words. Consistent with repo policy.

③ Boundary flags

open_questions is empty. The report's deviations are answered in the seat record: the screenshot and pixel height are not measured, by dispatch, and the claim is structural. The switch still shows enabled, and the card's targets 1–2 wait on objectstack-ai/objectstack#23842, as triage ruled. No flag is raised.

Implemented-by: claude/issue-12609-packaged-automation-rows
Reviewed-by: session_011K9g23W67dy2zwnyzaoB6o

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · domain:ui seat 1 · session_011K9g23W67dy2zwnyzaoB6o · 2026-10-16T10:16Z

What was reviewed:

  • Head ad380ce46dbbc2512e6b0fe9d651d461324075e8, against objectui#12609 as triage narrowed it at 6210990723 (target 3 only, the half that classifies nothing).
  • The claim 6212704910 (Clause-②: yes, mode:subagent) and dev report 6213026539.
  • The contract review of record, 6213273666 (TIER_DEFAULT, PASS, same head).
  1. Shape.
    • Fixes #12609 is the first line and the only closing keyword. Clause-②: yes starts a line, and the assignee is os-tesla.
    • All 13 files sit inside the claimed surface: PackagedAutomationPage.tsx (the flow rows and the module docblock only), its test, the ten locale packs, and one changeset. PackagedActionsSection.tsx is untouched; it renders no reason. The page header, which objectui#12606 owns, is untouched.
    • The changeset was checked sentence by sentence against the diff. No content/docs/releases/ change. The surface is NOT governed, at 207 changed lines.
  2. Premises measured by the seat.
    • The reason paragraph is removed from the flow cell. ReasonDetails draws a constant chip and a Popover that shows {reason} verbatim, with no alert role and no read of the sentence.
    • The chip's render rule is the existing joinPackagedFlows one, unchanged. Rows without a reason render the old markup.
    • One pack key is added and nothing else in the packs changes.
  3. Gates (check-runs pinned to this head).
    • 33 success and 3 skipped by design, including Lint, Repo Gates, Type Check, Test (shard 1–16/16), Test (dist pins), Build & E2E, Bundle Analysis, Changeset Bump Policy and Live E2E (informational).
    • The dev's four ablations each go red: 2/15, 3/15, 2/15 and 2/15.
  4. Accepted deviations.
    • Card acceptance 2, the 1280 px light and dark screenshots, and the row's pixel height are NOT MEASURED, as dispatched. What is pinned is structural: the row holds no paragraph, and the chip sits inline on the label's line. The PR lists what a reader needs to take the screenshot.
    • The whole-package @object-ui/app-shell test run is left to CI. All 16 shards pass on this head.
  5. Out of scope.
  6. Landing.
    • Ready, then auto-merge into the merge queue.
    • After the merge: verify by content, close the card through Fixes, and strip pm:dispatched.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 10:18
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 10:18
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 8a8bcf3 Oct 16, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-12609-packaged-automation-rows branch October 16, 2026 10:31
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.

Packaged automation: a flow disabled by deployment policy shows a status chip; the policy is explained once

2 participants