Repository navigation
Commit 8e35895
feat(spec): offer onSuccess and outcomeMessages in Studio's action form; field.picklist joins the own-editor class (#21901)
Fixes #21863
Clause-②: no
This lands the director seat's ruling 1A / 2B on the card (record
`5995552118`, maintainer 「同意」). Studio's action form now offers
`onSuccess` and `outcomeMessages`, using the controls ruling
`5861442317` already gave their shapes. The `field.picklist` ledger row
moves into the ruled class "authored through its own editor". No Zod
key, liveness row or gate logic changes.
## What changed
- **The form rows** (`packages/spec/src/ui/action.form.ts`), in the
Behavior section after `successMessage` / `errorMessage`:
- `outcomeMessages` is one `widget: 'json'` row, spelled like the file's
`patch` and `bodyExtra` rows (G1: a map-shaped value takes `json`). Its
helpText says each key is a snake_case `outcome` value the handler
returns, and each value is the label shown for that outcome. It also
says what happens with no entry, and that a key the handler never
returns is never shown.
- `onSuccess` is one `composite` row (G2: an object-shaped value takes
curated sub-rows), the face the `body` row above already uses. It has
two sub-rows:
- `navigate` is `type: 'text'`, `required: true`, because the Zod
requires it inside the block. Its helpText names the route or URL
template and its three interpolation scopes (`${param.*}`, `${ctx.*}`,
`${result.*}`), taken from the key's describe.
- `openIn` is `type: 'select'` with no inline `options`. The member
`newTab` has a capital letter, and `FormSelectOptionSchema.value` is a
lowercase system identifier, so an inline list cannot spell it. The
select reads its two members (`self`, `newTab`) off the served schema
instead. Their meanings are in the helpText, and nothing is retyped.
- Both rows carry `visibleWhen: "data.type == 'api' || data.type ==
'script'"`. These are the two types the parse accepts them on (the
`onSuccess` type refinement and `refuseInertOutcomeMessages` in
`action.zod.ts`), so the control is shown where the value is accepted.
- No designed control and no new widget.
- **The ledger**
(`packages/spec/src/system/metadata-form-zod-reconciliation.test.ts`):
- The `action.onSuccess` and `action.outcomeMessages` `omit` rows are
deleted from the "Declared, not enforced yet" group.
- **2B:** `field.picklist` is added to the "authored through its own
editor" key list in `RULED_ROOT_REASONS`, and `RULED_EDITORS` gains
`'field.picklist': { surface: "the object designer's shared-picklist
picker" }`.
- The picklist row moves into that class's group and its `why` is
rewritten. It states the `live` verdict (the server resolves the named
picklist onto the served field's `options` and judges a write against
that set). It names the surface, and gives the class's citation with the
placement ruling inside the same parenthetical: `(ruling record
5861442317, #19332; the picker placed there by ruling record 5755653853,
#18164 batch #209 item 1 A)`.
- Comments only: the four comment blocks that described the class as
holding exactly ruling `5861442317`'s keys now name `field.picklist` and
the ruling that admitted it (`5995552118`).
- `object.externalSharingModel` and `view.groups` are untouched.
- **The catalogs**: the four
`packages/platform-objects/src/apps/translations/*.metadata-forms.generated.ts`
files were regenerated with `node scripts/check-i18n-bundles.mjs --write
--filter=platform-objects`, following PR #21854:
- The first `--write` added the four new keys (`outcomeMessages`,
`onSuccess`, `onSuccess.navigate`, `onSuccess.openIn`). The zh-CN /
ja-JP / es-ES `label` and `helpText` leaves were then written in place.
- A second `--write` kept those leaves and dropped the eight provenance
entries per locale that the first extract had added to the
`*.source-hashes.generated.ts` companions.
- The three companions are byte-identical to `BASE`: blob `2decb02bf6f1`
(es-ES), `a96e8eb503d5` (ja-JP) and `31882e433b83` (zh-CN), before and
after.
- **Three count pins in `platform-objects`, each re-taken with one
comment line naming the rows:**
- The translated row-label positive control in
`object-lifecycle-panel-echo-decisions.test.ts`: 661 → 665 per locale
(four new row labels).
- In `action-body-panel-echo-decisions.test.ts`, the count of composite
children the repeater pin does not walk: 5 → 7 (`onSuccess.navigate`,
`onSuccess.openIn`).
- In the same file, the set of their parents: `['body']` → `['body',
'onSuccess']`.
The same file's echo rule ("no leaf on this panel reads its `en` source
unless the ledger decided it is an echo") stays green, so all eight new
leaves are authored in all three locales.
- **Changeset**: `@objectstack/spec` patch and
`@objectstack/platform-objects` patch, in one file (see What ships).
## Controls (reverse verification, from the committed state)
Both legs ran against HEAD `a5106c1c78`, in a throwaway detached
worktree at that commit, through `scripts/ablation-replace.mjs` in wrap
mode. Each leg also had its own `trap` that restores from `HEAD`.
Command: `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/system/metadata-form-zod-reconciliation.test.ts`, through
`scripts/pm/os-verify-lock.sh`.
| state | reading |
|---|---|
| baseline (unmutated) | 1 file, **76 passed (76)** |
| the `onSuccess` form row deleted (anchor x1 → x0, blob `da0842c71614`
→ `0c90358ff49f`) | **1 failed / 75 passed**: `action.(root): accepted
by the Zod but unauthorable in the form — offer it, or add a root ledger
entry that records why it is not offered: expected [ 'onSuccess' ] to
deeply equal []`. Restored: blob `da0842c71614` == HEAD, `git diff HEAD`
empty |
| `field.picklist` removed from the class's key list (anchor x1 → x0,
injected text x0 → x1 read on disk during the run, blob `c3bc2007ee93` →
`aa5ba299af81`) | **1 failed / 75 passed**: `"authored through its own
editor": the root rows giving this reason are not the keys ruling record
5861442317 put in it. A key the ruling did not name needs a ruling of
its own; …: expected [ 'field.picklist', …(4) ] to deeply equal [
'object.actions', …(3) ]`. Restored: blob `c3bc2007ee93` == HEAD, `git
diff HEAD` empty, `git status --porcelain` 0 lines |
The first run of leg 1 timed out in the lock queue (exit 99, never
acquired). The tool restored the file then (blob == HEAD), and the leg
was re-run once the lock freed; the reading above is the re-run. The
test imports only relative `src` paths and the spec package has no
workspace dependencies, so no `dist/` is involved in either leg.
## What ships
Measured with `npm pack --dry-run --json --ignore-scripts`, with a
positive control:
- `@objectstack/spec`: `src/ui/action.form.ts` is not in `files[]` (0
entries; `src/ui/action.zod.ts`, the control, is 1). The form ships
compiled, though. The new `onSuccess` and `outcomeMessages` helpTexts
are each in 6 packed `dist/` files, and the control, `successMessage`'s
helpText, is in 6.
- `@objectstack/platform-objects`: the new en helpText is in 6 packed
files, against 6 for the control. The zh-CN and ja-JP labels are in 6
each, matched as unicode-escaped text, and the es-ES label is in 6. The
control, zh-CN `successMessage`, is in 6.
Both packages publish the change, so `skip-changeset` does not apply.
## Verification (head `a5106c1c78`)
All suites ran through `scripts/pm/os-verify-lock.sh`.
- `pnpm --filter @objectstack/spec exec vitest run --project local
--maxWorkers=2`: **618 files passed, 18450 tests passed, 1 todo**.
- `pnpm --filter @objectstack/spec exec vitest run --project repo
--maxWorkers=2`: **53 files passed, 902 tests passed**.
- `pnpm --filter @objectstack/platform-objects exec vitest run
--maxWorkers=2`: **59 files, 949 passed**. Run before the pins were
re-taken, it gave exactly the three failures above (`expected 7 to be
5`, `expected [ 'body', 'onSuccess' ] to deeply equal [ 'body' ]`,
`zh-CN positive control: expected 665 to be 661`).
- `pnpm --filter @objectstack/metadata-protocol exec vitest run
--maxWorkers=2`: **215 files passed, 3 skipped; 27906 tests passed, 19
skipped**. No served-schema count moved: the change adds no Zod key.
- `pnpm --filter @objectstack/spec run typecheck`: exit 0. `tsc -p
tsconfig.test.json --listFilesOnly` compiles both changed spec files.
- `pnpm --filter @objectstack/platform-objects run typecheck`: exit 0.
Its `tsconfig.test.json` compiles both changed test files.
- `pnpm check:i18n`: exit 0, `platform-objects in sync (11 bundle(s))`.
- `pnpm check:i18n-coverage`: exit 0, `13 config(s), 621 baselined
untranslated string(s), none new`. The first attempt exited 3
(PREREQUISITE NOT MET, the example closure unbuilt), and the reading
here is from after the build.
- `pnpm --filter @objectstack/spec check:generated`: exit 0 (15
artifacts up to date).
- **Gates:** `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 87 commands from 9 paths against
merge base `607463d73`. All 87 ran on `a5106c1c78`. `--ran`
reconciliation: `87 derived famil(ies) accounted for — 87 run, 0
NOT-MEASURED`. `pnpm check:dual-build-cjs-loads` first exited 3
(PREREQUISITE NOT MET, no `dist/` for nine packages), and exited 0 after
a full workspace build. The record keeps that last reading.
- **eslint:** `--no-inline-config --format json` on the 8 changed TS
files gave 8 files, 0 errors, 0 warnings. The population is
`eslint.config.mjs:971` (`**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}`). The
config never enables type-aware linting (`eslint.config.mjs:326-328`: no
`parserOptions.project`), so the verdict on an untouched file cannot
move.
- **Patch round 1, at `bff24d6190`** (claim revisions `5999807822` and
`6000249318`; one commit on `a5106c1c78`): the two red shards on
`a5106c1c78` were this PR's own pins.
- `Test Core (4/6)`:
`packages/lint/src/validate-predicate-path-refs.test.ts` re-takes the
shipped-corpus pins 83 → 85 (predicates) and 58 → 62 (`==` / `!=`
literal comparisons), each with a history comment. Measured against
merge base `607463d736` by `<form>::<field>::<source>`: added exactly
`action :: outcomeMessages` and `action :: onSuccess`, both `data.type
== 'api' || data.type == 'script'`; none removed.
- `Test Core (2/6)`:
`packages/cli/test/i18n-extract-outcome-messages.test.ts` leaves the
`metadataForms.` subtree out of its `.outcomeMessages.` selection. The
new row's
`metadataForms.action.fields.outcomeMessages.{label,helpText,placeholder}`
paths are form-row text, so the equality stays over the four
action-outcome keys. Ablation control: with the clause made a no-op, the
file reddens exactly as CI did (`expected [ …(7) ] to deeply equal [
…(4) ]`).
- Local runs after a full build: `@objectstack/cli` 352 files, 4683
passed, 2 skipped; `@objectstack/lint` 119 files, 5629 passed; plus
`objectql`, `service-automation`, `plugin-approvals`,
`cloud-connection`, `http-conformance`, `plugin-dev`,
`example-embed-objectql`, `connector-rest`, `connector-slack` and
`rest`, all green. `dispatch-gates` derived 89 commands from 11 paths,
and all 89 exited 0. ESLint 0 / 0 on the 2 edited files. No source line
moves in `packages/lint` or `packages/cli`.
## Acceptance notes
- **The picklist citation.** The gate requires every row of a ruled
class to contain `5861442317` ("cite the ruling record that decided the
reason"). The dispatch asked that the row name the placement ruling
(`5755653853`). So the row carries both, in the one parenthetical the
class's citation form uses. The two-ruling spelling follows the
`object.listViews` row in the same class (`ruling record 5861442317,
#19332; per-arm view forms under the #19330 ruling, letter A`). The
ruling that admitted the key to the class (`5995552118`) is cited in
code comments only.
- **The picker does not exist yet.** At the objectui pin `0abd4f9f87`,
`ObjectFieldInspector` has the inline option editor and no
shared-picklist picker; the picker is objectui#10202 phase 2. The row's
`why` therefore says the offer "was decided as that picker", not that
the picker is there. Until it lands, `field.picklist` is authorable only
in source.
- **Renderer reading, not a browser run.** Read at the objectui pin, not
run in a browser. `SchemaForm.tsx` resolves a composite sub-row's schema
from the parent's `properties`. `select` is a passthrough widget, and
with no `fieldSpec.options` the select falls back to the sub-schema's
`enum`, so `openIn` should render `self` / `newTab`. The JSON Schema
this head emits (`packages/spec/json-schema/ui/Action.json`) carries
`onSuccess` as a plain object, with `navigate` required and
`openIn.enum` equal to `self` and `newTab`, which is the shape that
fallback reads. `json` is a passthrough widget too, so `outcomeMessages`
takes the same face as `patch` and `bodyExtra`. NOT MEASURED: a browser
run of either face.
- **Surface, stated.** The claim named `action.form.ts`, the four
catalogs, the reconciliation test, any `platform-objects` count pin, and
a changeset. `action-body-panel-echo-decisions.test.ts` is the second
`platform-objects` pin the new composite moves. It falls under the
claim's "any `platform-objects` count pin" and under the cross-lane
declaration `5998582591` on #6367. No metadata-protocol pin moved.
- `origin/main` gained one commit after the branch point (`87712ab823`,
global search in `metadata-protocol`). It touches none of this PR's
paths, so it was not merged.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3dbd084 commit 8e35895
11 files changed
Lines changed: 146 additions & 33 deletions
File tree
- .changeset
- packages
- cli/test
- lint/src
- platform-objects/src/apps/translations
- spec/src
- system
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
73 | | - | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
74 | 78 | | |
75 | 79 | | |
76 | 80 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
630 | 630 | | |
631 | 631 | | |
632 | 632 | | |
633 | | - | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
634 | 642 | | |
635 | 643 | | |
636 | 644 | | |
| |||
731 | 739 | | |
732 | 740 | | |
733 | 741 | | |
734 | | - | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
735 | 746 | | |
736 | 747 | | |
737 | 748 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
659 | 659 | | |
660 | 660 | | |
661 | 661 | | |
662 | | - | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
663 | 665 | | |
664 | 666 | | |
665 | 667 | | |
| |||
712 | 714 | | |
713 | 715 | | |
714 | 716 | | |
715 | | - | |
| 717 | + | |
| 718 | + | |
716 | 719 | | |
717 | 720 | | |
718 | 721 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1736 | 1736 | | |
1737 | 1737 | | |
1738 | 1738 | | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
1739 | 1755 | | |
1740 | 1756 | | |
1741 | 1757 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1736 | 1736 | | |
1737 | 1737 | | |
1738 | 1738 | | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
1739 | 1755 | | |
1740 | 1756 | | |
1741 | 1757 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1736 | 1736 | | |
1737 | 1737 | | |
1738 | 1738 | | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
1739 | 1755 | | |
1740 | 1756 | | |
1741 | 1757 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1171 | 1171 | | |
1172 | 1172 | | |
1173 | 1173 | | |
1174 | | - | |
| 1174 | + | |
| 1175 | + | |
| 1176 | + | |
| 1177 | + | |
1175 | 1178 | | |
1176 | 1179 | | |
1177 | 1180 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1736 | 1736 | | |
1737 | 1737 | | |
1738 | 1738 | | |
| 1739 | + | |
| 1740 | + | |
| 1741 | + | |
| 1742 | + | |
| 1743 | + | |
| 1744 | + | |
| 1745 | + | |
| 1746 | + | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
1739 | 1755 | | |
1740 | 1756 | | |
1741 | 1757 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
295 | 295 | | |
296 | 296 | | |
297 | 297 | | |
298 | | - | |
299 | | - | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
300 | 301 | | |
301 | 302 | | |
302 | 303 | | |
| |||
391 | 392 | | |
392 | 393 | | |
393 | 394 | | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
406 | | - | |
407 | | - | |
408 | | - | |
409 | | - | |
410 | | - | |
411 | | - | |
412 | | - | |
413 | | - | |
414 | | - | |
415 | 395 | | |
416 | 396 | | |
417 | 397 | | |
| |||
433 | 413 | | |
434 | 414 | | |
435 | 415 | | |
436 | | - | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
437 | 421 | | |
438 | 422 | | |
439 | 423 | | |
| |||
462 | 446 | | |
463 | 447 | | |
464 | 448 | | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
465 | 456 | | |
466 | 457 | | |
467 | 458 | | |
| |||
1411 | 1402 | | |
1412 | 1403 | | |
1413 | 1404 | | |
1414 | | - | |
| 1405 | + | |
| 1406 | + | |
1415 | 1407 | | |
1416 | 1408 | | |
1417 | 1409 | | |
| |||
1421 | 1413 | | |
1422 | 1414 | | |
1423 | 1415 | | |
1424 | | - | |
| 1416 | + | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
1425 | 1422 | | |
1426 | 1423 | | |
1427 | 1424 | | |
1428 | | - | |
| 1425 | + | |
1429 | 1426 | | |
1430 | 1427 | | |
1431 | 1428 | | |
| |||
1447 | 1444 | | |
1448 | 1445 | | |
1449 | 1446 | | |
| 1447 | + | |
1450 | 1448 | | |
1451 | 1449 | | |
1452 | 1450 | | |
| |||
0 commit comments