You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3b45d4a
Browse filesBrowse the repository at this point in the historyBrowse files
docs(skills): objectstack-ui and objectstack-i18n teach pressed, the five {{ }} copy slots, submitText, the thank-you keys, kanban.lanes and report seriesBy (#28488)
<!-- claude-projects-attribution: {"github_login":"os-tesla"} -->
_Requested by **Tesla** · [project
thread](https://claude.ai/code/project/chan_01AXsQkLFKRdQCqQ3RTr6Jao?thread=cmsg_01AXsQkLFKRdQCqQ3RTr6JaoX9GDX2cb55BEVTSE1eYheJ)_Fixes#28243Fixes#28464Fixes#28190Fixes#28215Fixes#28212Fixes#28207
Clause-②: no
Metadata-protocol: none
domain:skills#1 · os-tesla · session_01L9qibz4G9FrLR4TRExx6gn
## 维护者速读
- 改了什么:两个对外发布的技能包补教六个平台已经在画的键——动作的 `pressed`(本人已操作的切换态)、动作文案五个 `{{ }}`
槽位(`description`、参数 `helpText`、`outcomeMessages`)、参数对话框按钮文字 `submitText`
及其翻译槽位、表单感谢页的翻译键、看板 `kanban.lanes`、报表图表 `seriesBy`;附一条 `submitText` 的
eval。
- 为什么改:这些键在 spec 和控制台都已落地,但技能文本没教,AI 作者照技能写出的元数据要么用了 `${…}` 被 `os
validate` 拒收,要么绕路(用 `matrix` 代替
`seriesBy`,用过滤器想去掉空泳道),要么根本不知道可以改按钮文字、做切换态。
- 风险与回滚:纯文档,不改 schema、键或行为;三个零余量文件的新增全部以同文件删减支付,token 天花板一行不动。回滚 = 撤这个
PR。
- 先看:`skills/objectstack-ui/rules/actions.md`(六条 bullet 和 `ctx` 一节的压缩是风险
hunk);`skills/objectstack-ui/rules/dashboards.md` 的报表示例与 `seriesBy`
注;`skills/objectstack-i18n/SKILL.md` 的 `_views` / `_actions` 两行。
- 两分钟验证:`node scripts/check-skills-token-ratchet.mjs`,预期四个被改文件全绿且
headroom 为 4 / 21 / 2 / 687;`git grep -n
'seriesBy\|lanes:\|submitText\|pressed' skills/objectstack-ui
skills/objectstack-i18n` 每个键至少一处命中。
- 需要你做的:受管面(`skills/**`,Tier H)——席位做 Fable 契约复审并写席位核对后,由 os-zhuang
审批落地;dev 不翻 ready、不请求审阅。
## What changes
Six teaching edits, one commit per card (plus a one-line fix-up that
swaps a reserved word out of an example), in the files the fold order
names. Nothing under `skills/*/references/_index.md`.
| Card | Where | What the skill now says | Read against (`main`
82b29fe) |
|:--|:--|:--|:--|
| #28243 | `skills/objectstack-ui/rules/actions.md`, a bullet under
*Visibility, Disable & Feedback* | `pressed` is a CEL predicate over the
same scope as `visible`; while TRUE the record header draws the button
pressed (`aria-pressed`); display only, never a gate; a boolean literal
is refused; `active` / `activeWhen` / `pressedWhen` rename onto it; a
per-reader fact is a block declared under `attachedOnRead`, filled per
caller, read as `has(record.viewer) && record.viewer.vote == 'helpful'`
| `packages/spec/src/ui/action.zod.ts:1659-1683` (docblock + describe),
`:1013` (renames); `packages/spec/src/ui/action-pressed.test.ts:60`
(boolean refused); `packages/spec/src/data/object.zod.ts:2441-2470`,
`:3201` (`attachedOnRead`); renderer
`ui/packages/components/src/renderers/layout/containers.tsx:2414-2428`,
`:2468` (`page:header`) |
| #28464 | same file, the "Copy holes are `{{ }}`" bullet |
`confirmText`, `description` and a param's `helpText` read `record`;
`successMessage` and each `outcomeMessages` entry read `result` and
`record`; `errorMessage` takes none; one example per new slot;
`${record.name}`, `{name}`, logic in a hole and a blank sentence fail
`os validate` in all of them |
`packages/spec/src/ui/action-copy-template.ts:19-45` (roots per key),
`:56-70` (the doors refuse), `:143-145` (the reference examples);
`packages/spec/src/ui/action.zod.ts:179-182`, `:396`, `:1117`, `:1532` |
| #28190 | same file (`confirmText` and `submitText` bullets,
`ReassignLeadAction` example);
`skills/objectstack-ui/evals/views-apps-actions-pages.json` (eval 11);
`skills/objectstack-i18n/SKILL.md` (`_actions` row) | `submitText` is
the verb on the param dialog's submit button in place of "Confirm", an
I18nLabel, refused blank and refused without a non-empty `params`;
`submitLabel` / `confirmLabel` rename onto it; `confirmText` is the
param-less action's confirm dialog, refused beside `params`; translation
slot `objects.OBJECT._actions.ACTION.submitText` |
`packages/spec/src/ui/action.zod.ts:936-969` (docblock, blank refusal),
`:1010` (renames), `:1436` (describe), `:2248-2257` (`confirmText`
beside `params`), `:2269` (`submitText` without `params`);
`packages/spec/src/system/translation.zod.ts:220-223`, `:513`, `:1042`;
renderer
`ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:345-364`,
`ui/packages/app-shell/src/views/ActionParamDialog.submitText-28158.test.tsx:10-11`
|
| #28215 | `skills/objectstack-i18n/SKILL.md`, the `_views.{view_name}`
row | `submitBehavior.title` / `submitBehavior.message` translate the
thank-you panel of a form whose `submitBehavior` is `kind: 'thank-you'`;
only authored copy is translated; the other kinds carry none |
`packages/spec/src/system/translation.zod.ts:271-297` (the shape),
`:335`, `:386-402` (the `_views` member); renderer
`ui/packages/plugin-form/src/ObjectForm.tsx:637`, `:1328-1329` |
| #28212 | `skills/objectstack-ui/rules/list-views.md`, the kanban
example and the note under it | `lanes` lists the stored option values
to draw, in that order; a choice of lanes, not a record filter (an
unlisted value lands in `Uncategorized`; filter the view to keep records
off the board); omit it for one lane per option; a non-option or
repeated value fails `os validate` |
`packages/spec/src/ui/view.zod.ts:2320-2365`
(`KanbanConfigSchema.lanes`);
`packages/lint/src/validate-list-view-field-refs.ts:370-392`,
`:1083-1140` (the two refusals); renderer
`ui/packages/core/src/utils/kanban-declared-lanes.ts:10-40` |
| #28207 | `skills/objectstack-ui/rules/dashboards.md`, the report
example and the note under it | `seriesBy` splits the one `yAxis`
measure into a series per value of a second dataset dimension, not a
`matrix` workaround; it must name a declared dimension other than
`xAxis` (publish refuses an unknown name); refused on `pie` / `donut` /
`funnel` / `treemap` / `sankey`; with it, `series[].name` names one of
its values | `packages/spec/src/ui/report.zod.ts:33-57` (key +
describe), `:147-203` (`checkReportChartSeriesSplit`, the two arms from
PR #28439); `packages/lint/src/validate-chart-bindings.ts:589-604`
(undeclared dimension); renderer
`ui/packages/plugin-report/src/DatasetReportRenderer.tsx:1375-1395` |
**Premise re-checked before each commit:** `git grep` on `main` 82b29fe
finds none of `pressed`, `seriesBy`, `lanes` (kanban), `submitText`
(action), `description` / `helpText` / `outcomeMessages` as `{{ }}`
slots, or `submitBehavior.title` under `skills/` — the only prior
mentions are the generated `references/react-blocks.md` rows for the
`object-form` block and the `_actions` row's `outcomeMessages.{outcome}`
translation key. Every member's `Blocked-by:` is closed (#28242 / PR
#28301, PR #28463, PR #28188 + #28158, PR #28195, #28211 / PR #28291,
#28206 / PR #28305). No member dropped.
### Token ratchet — paid by deletion in the same file
`scripts/check-skills-token-ratchet.mjs` prices every `rules/**` file
(convention `ceil(utf8 bytes / 4)`, the SKILL.md stamp line excluded).
The three files this fold grows sat at 1 / 0 / 3 tokens of headroom, so
each addition is paid inside its file; the eval fixture is measured, not
ratcheted (ruling record 6513306068). No ceiling row moves.
| File | Tokens before → after (ceiling) | Lines before → after |
Deletions that pay, and where the dropped text still lives |
|:--|:--|:--|:--|
| `skills/objectstack-ui/rules/actions.md` | 2258 → 2255 (2259) | 204 →
179 | ① the bare-CEL paragraph under the bullet list folds into the
`visible` bullet (same rule, one place); ② the `ctx` section drops its
blessed-name code block and the restated hook axes —
`skills/objectstack-data/references/data-hooks.md:362-363`, `:588-608`
carry them — and keeps the trusted-execution and ADR-0095 `positions`
rules in one paragraph; ③ the `openIn` / `opensInNewTab` prose, the "two
mechanisms" lead-in, the new-tab-via-`params` warning and the
field-backed-params paragraph are condensed (every rule kept: static vs
computed URL, pre-open, `newTabUrl` shape and refusal, own-auth
endpoint, the checkbox mis-render, `objectOverride`, `defaultFromRow`);
④ two "Always"/"Best practice" lead-ins trimmed |
| `skills/objectstack-ui/rules/list-views.md` | 3129 → 3108 (3129) | 311
→ 309 | the "Key rule" blockquote (groupByField is a select, one column
per option) folds into the example's own comment and the new `lanes`
note; the ADR-0047 bullets "toggle is deprecated" and "the switcher
renders as a compact dropdown … a single-entry whitelist locks the
visualization" fold into the two code comments that already name
`element` and `allowedVisualizations` |
| `skills/objectstack-ui/rules/dashboards.md` | 6259 → 6260 (6262) | 464
→ 463 | the "Engine support" bullet (per-driver bucket functions:
`to_char`, `date_format`, `strftime`, `$dateToString`, `bucketDateKey`)
and the "Studio's Dashboard Widget Inspector …" bullet — both describe
the engine and the Studio, not anything an author writes; the
bucket-label table above keeps the author-facing fact |
| `skills/objectstack-i18n/SKILL.md` | 5571 → 5646 (6333) | 524 → 524 |
none needed (687 headroom) |
| `skills/objectstack-ui/evals/views-apps-actions-pages.json` | 2108 →
2320 (not ratcheted) | 105 → 115 | eval 11 added |
Whole package: every `skills/*/SKILL.md` summed, 4607 → 4607 lines,
226235 → 226537 bytes; the gate's bundle total (all shipped files, its
own convention) 219828 → 220092 tokens (the before figure is the after
figure minus the five per-file deltas above; the gate prints the after
figure).
## Tests
- `node scripts/check-skills-token-ratchet.mjs` — exit 0: *56 authored
bundle file(s) within their ceilings*; the four changed authored files
read headroom 4 / 21 / 2 / 687.
- `skills/objectstack-ui/evals/views-apps-actions-pages.json` stays
valid JSON (parsed after the edit); eval 11 asks for a param dialog
whose button reads the verb and asserts `submitText: 'Log Call'` beside
`params`, refusing `submitLabel` / `confirmLabel` / `confirmText`.
- The two `os:check` blocks this PR edits (`ReassignLeadAction` gains
`submitText`; the report example gains `seriesBy`) compile under `pnpm
--filter @objectstack/spec run check:skill-examples` — see Verification.
- No ablation: a docs-only diff changes no tested behaviour.
## Acceptance notes
- **`skills/objectstack-i18n/evals/bundles-locales-coverage.json`
unchanged.** The order allowed an i18n eval "if the gate asks"; no
derived gate asks for one, and the two new slots are table rows, so none
was added. Noted, not filed.
- **The `description` copy-hole example reads "owner", not "role".** The
first spelling (*Choose the new role for …*, the spec's own reference
example) tripped `check:role-word` (ADR-0090 D3 ratchets the word in
published prose), so the example names the owner instead; the fix-up is
its own commit because the branch is shared under AGENTS.md §3 and was
not rewritten.
- **`pressed` scope.** The card says "`record` and `user`"; the schema
docblock says "the same scope as `visible` and `disabled`", and
`visible`'s own describe names `record` + `current_user`, so the rule
says "the same scope as `visible`" rather than naming a root the card
spelled differently.
- **`confirmText` beside `params` is refused** (`action.zod.ts:2248`) —
a fact one step outside the six cards, carried into the `confirmText`
bullet because it is the contrast #28190 asks for ("It is not
`confirmText`"). Two words, no new scope.
- **The deleted `ctx` code block** was the one place in `skills/` that
spelled `ctx.user?.organizationId ?? ctx.session?.organizationId` as a
fenced line; the expression now stands inline in the same paragraph, and
`data-hooks.md` keeps the fenced form.
- Out-of-scope findings: none.
## Verification
All readings at head `162680ed` (worktree
`/home/user/objectstack-28243`, base `origin/main` 82b29fe).
<details><summary>Builds that the three dist-reading gates needed (under
<code>scripts/pm/os-verify-lock.sh</code>)</summary>
- `pnpm --filter '@objectstack/lint...' build` — VERDICT command-exit 0,
held the lock 102 s.
- `pnpm build --concurrency=2` (127 packages in scope, docs excluded) —
VERDICT command-exit 0, held the lock 12m28s, *Tasks: 116 successful,
116 total*. Needed because `check:dual-build-cjs-loads`,
`check:lean-entry-closure` and `check:skill-examples` each exited 3
(PREREQUISITE NOT MET: no `dist/` for 89 packages, no
`packages/objectql/dist/core.mjs`, no `.d.ts` under
`packages/client-react/dist`) on the first sweep; they were re-run after
the build and recorded with their re-run codes.
</details>
<details><summary>Derived gates (<code>dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack</code>, 45 lines at 162680e, each run
with stdin closed and its exit captured before any pipe)</summary>
Every one of the 45 lines is `:: exit 0`. The record is
`/mnt/project-files/skills-seat-1/r2026-10-31/out/28243-gates-ran-162680ed.list`
in the seat's project folder.
`dispatch-gates.mjs --repo objectstack-ai/objectstack --ran` on that
record: *45 derived famil(ies) accounted for — 45 run, 0 NOT-MEASURED (a
DERIVED zero — all 45 recorded an exit code and none of them is 3)*,
exit 0.
Readings worth quoting: `node scripts/check-skills-token-ratchet.mjs` —
*56 authored bundle file(s) within their ceilings; 10 generator-owned
file(s) and 28 eval fixture(s) measured, not ratcheted*; `pnpm
check:role-word` — exit 0 at this head (exit 1 at 5c2fb7d on the first
`description` example, fixed in 162680e); `pnpm
check:skill-identifier-liveness`, `pnpm check:pm-skill-id-lint`, `pnpm
check:doc-authoring`, `pnpm check:skill-frame-sync`, `pnpm
check:corpus-claim-drift`, `pnpm --filter @objectstack/spec run
check:skill-docs` — exit 0.
</details>
<details><summary>Owed outside the derivation: the <code>os:check</code>
example compile</summary>
`pnpm --filter @objectstack/spec run check:skill-examples` — exit 0:
*277 marked example(s) across 113 file(s), 3 surface(s)* … *277 prose
examples type-check across 3 surface(s) — every marked block parsed, so
tsc ran the SEMANTIC pass on all of them*. The two blocks this PR edits
(`ReassignLeadAction` with `submitText`, the `PipelineCoverageReport`
with `seriesBy`) are among them. Recorded in
`28243-gates-ran-162680ed-extras.list` beside the derived record.
</details>
Every other family is CI's: see this PR's check-runs. No test suite was
run locally: the diff touches no package source (`skills/**` and one
changeset), so there is no affected package to filter to; `Test Core`
and the rest run on the PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L9qibz4G9FrLR4TRExx6gn)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
docs(skills): objectstack-ui and objectstack-i18n teach six keys the platform already draws — `pressed`, the `{{ }}` copy slots `description` / `helpText` / `outcomeMessages`, the param dialog's `submitText` and its translation slot, the form thank-you translation keys, `kanban.lanes` and report chart `seriesBy`
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
-**Before.** Each key shipped in the spec and the console drew it, but no published skill taught it: an agent authoring from the skills wrote a toggle header action with no `pressed`, spelled the dialog line, a param hint or a per-outcome toast with `${…}` holes that `os validate` refuses, left the param dialog's button at "Confirm", translated no thank-you panel, could neither order kanban lanes nor drop an always-empty one, and split a chart by a second dimension with a `matrix` workaround.
11
+
-**Now.**`objectstack-ui/rules/actions.md` teaches `pressed` (display-only reader state over an `attachedOnRead` block; a boolean literal is refused), names all five `{{ }}` copy slots with one example each, and teaches `submitText` beside `params` (with the `confirmText` contrast and the refused spellings); its `ReassignLeadAction` example sets `submitText`, and eval 11 asks for a param dialog whose button reads the verb. `rules/list-views.md` shows `kanban.lanes` in the kanban example — a choice of lanes and their order, not a record filter. `rules/dashboards.md` sets `seriesBy` on the report chart example and says where it is refused (an undeclared dimension, the `xAxis` dimension, a single-series type). `objectstack-i18n/SKILL.md` lists `_views.{view}.submitBehavior.title` / `.message` and `_actions.{action}.submitText`.
12
+
- Every file stays at or under its token ceiling: the actions, list-views and dashboards additions are paid for by condensing text in the same file (the `ctx` section's blessed-name code block, which restates the objectstack-data hooks reference; two ADR-0047 renderer notes folded into the code comments they repeat; two engine- and Studio-facing notes that told an author nothing to write). No ceiling moves, no frontmatter changes, no schema, key or behaviour changes.
|`fields.{field_name}`|`label`, `help`, `placeholder`, `options` (option value → label), `inlineTitle` (the heading of the inline child grid a `master_detail` field draws on its parent's form — the field lives on the child object, so the key is the child's; untranslated it keeps its authored text, never the child's plural label) per field |
163
-
|`_views.{view_name}`|`label`, `description`, `title` (a form view's own heading — only a form that authors one is translated; a list view has none), `emptyState.title` / `emptyState.message`, `bulkActions.{def_name}` (`label`, `confirmText`, `confirmLabel`, `params`) |
164
-
|`_actions.{action_name}`|`label`, `description`, `confirmText`, `successMessage`, `disabledReason` (the sentence a greyed action shows; absent keeps the console's default), `params.{param_name}` (and `.options.{value}` beneath it — an option's stored `value` in that param's inline `options`), `outcomeMessages.{outcome}` (an outcome the action's own `outcomeMessages` declares), `resultDialog.title` / `.description` / `.acknowledge` (only under an action that declares a `resultDialog`), `resultDialog.fields.{path}` (the literal `path` of a field in `resultDialog.fields[]`, dots included — `client.secret` is one key); the same keys hold under `globalActions.{action_name}`|
163
+
|`_views.{view_name}`|`label`, `description`, `title` (a form view's own heading — only a form that authors one is translated; a list view has none), `submitBehavior.title` / `submitBehavior.message` (the thank-you panel of a form whose `submitBehavior` is `kind: 'thank-you'`; only the copy the form authors is translated, and the other kinds carry none), `emptyState.title` / `emptyState.message`, `bulkActions.{def_name}` (`label`, `confirmText`, `confirmLabel`, `params`) |
164
+
|`_actions.{action_name}`|`label`, `description`, `confirmText`, `submitText` (the param dialog's button verb — only under an action that declares `params`), `successMessage`, `disabledReason` (the sentence a greyed action shows; absent keeps the console's default), `params.{param_name}` (and `.options.{value}` beneath it — an option's stored `value` in that param's inline `options`), `outcomeMessages.{outcome}` (an outcome the action's own `outcomeMessages` declares), `resultDialog.title` / `.description` / `.acknowledge` (only under an action that declares a `resultDialog`), `resultDialog.fields.{path}` (the literal `path` of a field in `resultDialog.fields[]`, dots included — `client.secret` is one key); the same keys hold under `globalActions.{action_name}`|
165
165
|`_sections.{section_name}`| Form section `label`, `description`; `fields.{field_name}` — the `label`, `placeholder`, `helpText` an entry overrides for that form alone (`helpText` here, `help` under `fields.{field_name}` above: each spells what its source does) |
Copy file name to clipboardExpand all lines: skills/objectstack-ui/evals/views-apps-actions-pages.json
+10Lines changed: 10 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -100,6 +100,16 @@
100
100
"must_contain": ["type: 'report'"],
101
101
"must_not_contain": ["reportName:"]
102
102
}
103
+
},
104
+
{
105
+
"id": 11,
106
+
"prompt": "Add a 'Log Call' action on `contact` that asks for the call notes and the outcome in a dialog, and make the dialog's button read Log Call instead of Confirm.",
107
+
"expected_output": "One `defineAction` with field-backed `params` (`call_notes`, `call_outcome`) and `submitText: 'Log Call'` beside them — the verb on the param dialog's submit button, an I18nLabel translated at `objects.contact._actions.log_call.submitText`. Not `confirmText` (the message of a param-less action's confirm dialog, refused beside `params`), not `submitLabel` / `confirmLabel` (refused, naming `submitText`), and never blank.",
Copy file name to clipboardExpand all lines: skills/objectstack-ui/rules/actions.md
+34-59Lines changed: 34 additions & 59 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,19 +15,16 @@
15
15
16
16
## Visibility, Disable & Feedback
17
17
18
-
-`visible` — CEL predicate (prefer the `P\`...\`` tagged template); when false the action is **hidden**.
18
+
-`visible` — a bare CEL predicate over the current record (prefer the `P\`...\`` template; `record.<field>` resolves identically on every surface, never `${…}`- or `{…}`-wrapped — see `objectstack-formula`); when false the action is **hidden**.
19
19
-`disabled` — `boolean`**or** a CEL predicate; when true the action **shows but greys out**. Use this (not `visible`) when the action should stay discoverable but locked in the current state.
20
-
-`disabledReason` — an I18nLabel (a plain sentence or an inline locale map) a greyed action shows as its hover title and accessible description. Display-only: never a predicate and never a gate (the hook or permission still refuses). Omit it to keep the console's default "Not available for this record"; a blank one is refused, and `disabledMessage` / `disabledTooltip` / `disabledHint` / `disabledText` are refused naming this key. The reason goes here, not in `description`.
21
-
-`confirmText` — set for any destructive or irreversible operation.
22
-
-`successMessage` / `errorMessage` — toast copy on success / failure. Always set `successMessage` for non-obvious outcomes; without it the UI shows a generic "Action completed" toast.
23
-
- Copy holes are `{{ }}`: `confirmText` reads `record`, `successMessage``record` and `result` (the handler's answer); `errorMessage` takes none. A `bulkActionDefs` entry's `confirmText` reads `{{ count }}` and its params by bare name (`{{ tier }}`). `${record.name}`, `{name}`, logic in a hole and a blank sentence fail `os validate`.
20
+
-`disabledReason` — an I18nLabel (a sentence or an inline locale map) a greyed action shows as its hover title and accessible description. Display-only: never a predicate and never a gate (the hook or permission still refuses). Omit it to keep the console's default "Not available for this record"; a blank one is refused, and `disabledMessage` / `disabledTooltip` / `disabledHint` / `disabledText` are refused naming this key. The reason goes here, not in `description`.
21
+
-`pressed` — a CEL predicate over the same scope as `visible` that marks the action as already done by this reader: while TRUE the record header draws it pressed (`aria-pressed`). Display-only, never a gate; a boolean literal is refused (a constant is a style, not a reader's state), and `active` / `activeWhen` / `pressedWhen` are refused naming this key. A per-reader fact (the reader's vote) is a block the object declares under `attachedOnRead`, filled per caller by a service or `afterFind` hook, read as `has(record.viewer) && record.viewer.vote == 'helpful'`.
22
+
-`confirmText` — set for any destructive or irreversible operation; the confirm dialog of a param-less action. A registered action is refused it beside a non-empty `params` (put the question on `description`); a view's `bulkActionDefs` entry pairs the two by its own contract.
23
+
-`submitText` — the verb on the param dialog's submit button ("Send", "Log Call") in place of "Confirm": an I18nLabel, refused blank and refused without a non-empty `params` (no dialog opens); `submitLabel` / `confirmLabel` are refused naming this key.
24
+
-`successMessage` / `errorMessage` — toast copy on success / failure. Set `successMessage` for non-obvious outcomes; without it the UI shows a generic "Action completed" toast.
25
+
- Copy holes are `{{ }}`: `confirmText`, `description` (`'Choose the new owner of {{ record.name }}.'`) and a param's `helpText` (`'Why {{ record.name }} is paused'`) read `record`; `successMessage` and each `outcomeMessages` entry (`resent: 'Invitation resent to {{ result.email }}'`) read `result` (the handler's answer) and `record`; `errorMessage` takes none. A `bulkActionDefs` entry's `confirmText` reads `{{ count }}` and its params by bare name (`{{ tier }}`). In all of them `${record.name}`, `{name}`, logic in a hole and a blank sentence fail `os validate`.
24
26
-`undoable: true` — an **Undo** in the success toast (and `Ctrl+Z`). Snapshot: runtime on `operation: 'update'`, console on `type: 'api'`; else refused.
25
27
26
-
Predicates are **bare CEL** — `record.status == "converted"`, evaluated against
27
-
the current record. `record.<field>` resolves identically on every surface
28
-
(`record_header`, `list_item`, …); prefer it over the bare-field form. Never
29
-
wrap a predicate in `${…}` or `{…}` braces (see `objectstack-formula`).
0 commit comments