Repository navigation
Commit d68d129
feat(spec): InlineActionSchema picks
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9X2jU4uniTTvc92oRkTrTKD)_
Fixes #29248
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: none — no `skills/**` file and no hand-written
`content/docs` page lists the inline action's key set; the only lists
are the generated `content/docs/references/ui/{action,component}.mdx`
tables, regenerated here.
## 维护者速读
- 改了什么:页面上内联声明的动作(`element:button` 的 `properties.action`)现在可以写
`description`,即参数对话框标题下那行说明;同时 `os validate` / 发布门对它的检查与已注册动作一致。
- 为什么改:您在 cloud#3281 裁定
A,先放宽上游契约。「邀请成员」对话框现在只显示通用提示,说不清邀请会发生什么;没有这个键,云端无从写这句话。
-
风险与回滚:纯放宽,旧元数据全部照常通过;只多接受一个键,其余未选的键(`icon`、`variant`、`body`、`cancelText`、`submitText`)仍被拒。已知缺口:控制台尚未把内联的多语言
map 折成当前语言,等 UI 半张卡落地。回滚即 revert 本 PR。
- 先看:`packages/spec/src/ui/action.zod.ts`(`InlineActionSchema` 的 pick
与上方注释)、`packages/lint/src/validate-action-copy-templates.ts`(`INLINE_KEYS`)、`packages/spec/src/ui/inline-action.test.ts`(新用例与对照组)。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/ui/inline-action.test.ts`,预期全部通过,含「`description` says what the
action does」一组 10 条。
- 需要你做的:无,席位落地。
## What changes
- `packages/spec/src/ui/action.zod.ts`: `InlineActionSchema`'s
`.pick({…})` gains `description: true`. It is `ActionSchema`'s own field
(the `template` slot with the inline locale-map arm), so the describe
text, the parse and the refusals are shared, not restated. The derived
`InlineAction` / `InlineActionParsed` types follow from the pick.
- The schema's docblock records why: the `element:button` renderer's
forward list (`ui/packages/components/src/renderers/basic/elements.tsx`)
already passes `action.description` to the runner, and the console's
param-collection handler
(`ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx`) draws it
under the dialog title. It also states the one window that remains: the
inline copy localizer
(`ui/packages/react/src/hooks/useActionTextLocalizer.ts`) collapses only
`confirmText`, `successMessage` and `errorMessage` (its
`DECLARED_COPY_KEYS`), so an inline locale map on `description` reaches
the dialog unresolved until #29249 lands. Spec accepts ahead of the
renderer, never the reverse.
- Same docblock, in place: the `bodyExtra` paragraph said the forward
list "does not yet include `bodyExtra`". Measured false on main
`27c1c555` (`elements.tsx` forwards `bodyExtra: action.bodyExtra`), so
it now says that window closed. Comment only.
- `packages/lint/src/validate-action-copy-templates.ts` (own commit; the
seat added it to the claim's file surface): `INLINE_KEYS` gains
`description`, and the two comments that said the inline pick has no
`description` are corrected. Without this, an inline `description` with
a `${…}` token, a hole over an unbound root or a blank text parses (the
slot keeps its blank arm, as on a registered action) and would pass
`objectstack validate`, `compile` and the publish gate, while the same
text on a registered action is an error there. The describe text that
renders into the InlineAction reference table says those doors name it,
so leaving lint alone would ship declared ≠ enforced.
- Regenerated: `content/docs/references/ui/action.mdx` and
`content/docs/references/ui/component.mdx` (one `description` row each
in the InlineAction tables), `packages/spec/authorable-surface/ui.json`
(`ui/InlineAction:description`).
- `.changeset/29248-inline-action-description.md`: `@objectstack/spec`
and `@objectstack/lint`, minor.
- Not touched, per the card: `ui/**` (#29249) and cloud. `cancelText`
and `submitText` are not picked (see Acceptance notes).
## Tests
Forced edits (they pinned the old pick):
- `packages/spec/src/ui/inline-action.test.ts` `INLINE_FIELDS` gains
`'description'` ("admits exactly the documented field set"), and the
"admits only fields ActionSchema itself declares" body gains a
`description` so the round-trip covers it.
- `packages/spec/src/ui/action-description.test.ts` "an INLINE action
still refuses it" pinned the refusal this card removes. It now asserts
the inline parse keeps the description as its template envelope; the
file header no longer lists "widen the INLINE surface" among what the
key's change must not do.
New:
- `inline-action.test.ts` "InlineActionSchema — `description` says what
the action does" (10 cases): a plain string parses and is kept as the
`template` envelope; a locale map is kept verbatim; no `description`
still parses and adds none; a non-string value is refused inline at the
same path with the same code as on `ActionSchema`;
`ElementButtonPropsSchema` carries it to parse output; CONTROL: `icon`,
`variant`, `body`, `cancelText`, `submitText` are each still
`unrecognized_keys`, naming exactly that key.
- `inline-action-type.test.ts`: two bodies typed through `InlineAction`
carry `description` (locale map, plain string), compiled by the package
typecheck and parsed by the door.
- `packages/lint/src/validate-action-copy-templates.test.ts`: an inline
`properties.action` `description` is judged like a registered one (a
`${…}` token, a `${…}` inside a locale-map value at `description.zh-CN`,
a blank sentence and a `{{ result.* }}` hole are each one error; a bound
`{{ record.* }}` hole and a clean locale map draw nothing); and the page
publish gate answers an inline `${…}` description with one error and no
advisory.
## Acceptance notes
- Premise, measured on main `27c1c555` with the spec dist built from it:
an inline `description` was REFUSED, not stripped.
`InlineActionSchema.safeParse({ type: 'api', target: 'invite_user',
description: 'Sends an e-mail invitation.' })` answered
`unrecognized_keys` with `keys: ["description"]`, for the plain string
and the locale map alike. So nothing an author wrote was silently lost;
a page that declared it failed validation. On the head the same calls
parse and keep it (string → `{ dialect: 'template', source }`, map
verbatim).
- The console's param dialog also draws `submitText` and `cancelText`
(`useConsoleActionRuntime.tsx`). Neither is picked by
`InlineActionSchema` nor forwarded by `element:button`, so an inline
action cannot set the dialog's button labels. Nothing is declared that
is not read, so this is no defect; noted for whoever widens the inline
dialog copy next.
- The inline refusal of `body` prints "Did you mean `body` → `mode`?":
the suggestion list is computed from `ActionSchema`'s full shape, and
`mode` is not on the inline pick either, so following it draws a second
refusal. Loud, not silent; noted only.
- `ui/scripts/check-action-forward-parity.mjs` reads the inline owed set
from the built spec: `element:button` now owes 13 (was 12) and still
forwards all of them, exit 0.
## Verification
Final head `7b596699`. The full suites ran at `89985489`; the only
commit after it moves one spec test into its own `describe` (test file
only), so the three touched spec test files, the spec typecheck and all
gates were re-run on `7b596699`.
<details><summary>Spec, lint and ui consumers</summary>
- `pnpm --filter @objectstack/spec test` at `89985489`: Test Files 790
passed (790), Tests 23392 passed, 1 todo. At `7b596699`:
`inline-action.test.ts`, `action-description.test.ts`,
`inline-action-type.test.ts` 3/3 files, 60/60 tests.
- `pnpm --filter @objectstack/spec typecheck` exit 0 at `89985489` and
at `7b596699` (it compiles `inline-action-type.test.ts`, so the two
`description` bodies typed as `InlineAction` are judged by tsc).
- `pnpm --filter @objectstack/lint test` at `89985489`: Test Files 189
passed (189), Tests 7827 passed, 5 skipped. `pnpm --filter
@objectstack/lint typecheck` exit 0.
- ui consumers that parse or forward an inline action, run from `ui/`
against the rebuilt spec dist: `elementButtonActionMembers-8071`,
`action-bodyShape-forward`, `action-onSuccess-forward`,
`action-outcomeMessages-forward-11344`, `action-undeclared-keys-8648`,
`block-config`, `block-config-i18n`, `PageBlockInspector.i18n`: 8/8
files, 126 tests passed. (The first run of `PageBlockInspector.i18n`
could not resolve `@objectstack/lint` before lint's own dist was built;
re-run after the build, 22/22.) The full ui suites are left to CI's Test
UI.
- `node ui/scripts/check-action-forward-parity.mjs` exit 0:
`element:button owes 13, forwards 18` (was 12 on main);
`check-component-surface-parity.mjs` report-only, exit 0.
- `packages/metadata*`: no fixture pins the inline key set (`git grep`
for `InlineAction` and the pick's key list outside spec, lint and ui
finds none), so nothing to run there.
</details>
<details><summary>Reverse verification</summary>
Both through `scripts/ablation-replace.mjs` (anchor hit 1 → 0, blob
restored to HEAD, `git diff HEAD` empty after each):
- Drop `description: true,` from the pick → `inline-action.test.ts`,
`action-description.test.ts`, `inline-action-type.test.ts`: 8 failed, 52
passed. Red: the field-set pin, the plain-string, locale-map,
same-refusal and `element:button` cases, the flipped inline pin, and
both typed bodies' runtime half. The five CONTROL rows and "still parses
without one" stay green, as they should.
- Drop `'description'` from lint's `INLINE_KEYS` →
`validate-action-copy-templates.test.ts`: 2 failed, 23 passed (exactly
the two new inline-description cases).
</details>
<details><summary>Gates and generated artifacts</summary>
- `pnpm --filter @objectstack/spec build` + `check:generated` at
`89985489`: 1 of 14 stale (`check:docs`), regenerated with `--fix` and
committed; then 14/14 up to date. At `7b596699`, after a rebuild:
`check:generated` exit 0, no tracked file moved.
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack`: 103 derived, the same list at both heads.
On `7b596699`: 103 run, every exit 0, `--ran` reconciliation "103 run, 0
NOT-MEASURED (a DERIVED zero)". Seven dist-reading spec gates first
answered exit 3 (PREREQUISITE NOT MET: the dist predated the moved test
file) and were re-run after the spec rebuild, exit 0 each. On `89985489`
the same 103 also all exited 0.
- `check-changeset-no-major --event` on this body: exit 0, LEVEL AXIS
reads `yes (widening)` + `added`, no package graded `patch`.
`check-partof-closing-keyword` on this body: exit 0.
- Local-only: `check:skill-frame-freshness` exit 0. `check:dev-prereqs`
exit 1: 35 `ui/` workspace packages have no `dist/` in this container
(only `packages/**` was built); the spec dist is fresh. A local state
reading, not a finding about this diff.
- Dogfood: not owed (no `!` in the title, no `(narrowing)` arm).
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmT9dnPvF2SeYafuvmH)_
---------
Co-authored-by: Claude <noreply@anthropic.com>description, so an inline action's param dialog can say what it does (#29369)1 parent f72bc9e commit d68d129
10 files changed
Lines changed: 208 additions & 32 deletions
File tree
- .changeset
- content/docs/references/ui
- packages
- lint/src
- spec
- authorable-surface
- src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
354 | 354 | | |
355 | 355 | | |
356 | 356 | | |
| 357 | + | |
357 | 358 | | |
358 | 359 | | |
359 | 360 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
| 236 | + | |
236 | 237 | | |
237 | 238 | | |
238 | 239 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
317 | 317 | | |
318 | 318 | | |
319 | 319 | | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
320 | 361 | | |
321 | 362 | | |
322 | 363 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
63 | | - | |
| 64 | + | |
64 | 65 | | |
65 | 66 | | |
66 | | - | |
67 | | - | |
| 67 | + | |
| 68 | + | |
68 | 69 | | |
69 | 70 | | |
70 | 71 | | |
71 | | - | |
72 | | - | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
73 | 79 | | |
74 | 80 | | |
75 | 81 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
666 | 666 | | |
667 | 667 | | |
668 | 668 | | |
| 669 | + | |
669 | 670 | | |
670 | 671 | | |
671 | 672 | | |
| |||
0 commit comments