Repository navigation
feat(spec): an action's param dialog can name its own submit verb — submitText - #28188
Conversation
…ubmitText Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
…t types the overlay Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
…tion-submit-label
…fered Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
📓 Docs Drift CheckThis PR changes 2 package(s): 14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e0ea8d88153a87cfd73692c265412c302afc2bcf && git checkout e0ea8d88153a87cfd73692c265412c302afc2bcf
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 501c864dd5918a7bab1f3a59e1246b8a0ddff48c 301f5b8c5bde38cb4c094f02703ef429ad3ea6c4 && git checkout -B drift-repro 501c864dd5918a7bab1f3a59e1246b8a0ddff48c && git merge --no-ff 301f5b8c5bde38cb4c094f02703ef429ad3ea6c4
node scripts/docs-audit/affected-docs.mjs --json 501c864dd5918a7bab1f3a59e1246b8a0ddff48c
|
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V 维护者速读(席位终稿 · head
|
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V CI note · 2026-10-30T16:06Z · head |
…tion-submit-label
…fter merging main The merge took the branch's side of two regenerated files and dropped main's `KanbanConfig:lanes` and `ReportChart:seriesBy` entries; gen:schema and gen:docs put them back. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V New head · 2026-10-30T17:54Z · Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429 "You've reached your Fable limit", 2026-10-30T04:26Z) · head f485240 |
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V CI note · 2026-10-30T18:13Z · head |
…tion-submit-label # Conflicts: # packages/spec/src/system/metadata-form-zod-reconciliation.test.ts
…erging main Main's side of the three regenerated files, re-derived from the merged tree so this branch's `submitText` rows sit beside main's new entries. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V New head · 2026-10-30T20:40Z · Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429 "You've reached your Fable limit", 2026-10-30T04:26Z) · head 528f8bf |
|
domain:spec#4 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
domain:spec#4 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: card #27944 (body, claim 6522879044, os-dev-report 6527066768), the seat's ACCEPT record 6527138419, its CI notes 6527387954 and 6530150750 (two main-reds inherited through the merge check, both since fixed on main) and its new-head notes 6529753660 and 6533023900 (two merges of main, regenerated files only, one hand conflict kept both omit rows), the PR body, the 21-file diff against merge base 49a078e, main's own movement since that base (265 files, sixteen shared with this diff, see ③), the head's check-runs (read 2026-10-31T02:31Z: check-runs on 528f8bf: 65 total, 58 success (Auto Label, Build & E2E UI, Build Core, Build Docs, Build UI Deps, Check Changeset, Check Documentation Links, Check PR Size, Dogfood Regression Gate, Flag docs affected by code changes, Governed Surface Queue Guard, Lint & Repo Gates, Lint UI, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, Live E2E UI, PR-body guards (claim · duplicate · Part-of · single-writer path), Plan Test Core shards, Spec property liveness, Test Core, Test Core (all 8), Test UI, Test UI (all 16), Type Check UI, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, TypeScript Type Check, filter); 7 skipped path-filtered (18.x upgrade harness (opt-in), Dogfood Regression Gate (matrix shard/3), Dogfood Verify CLI, Packed-tarball smoke (opt-in), Schedule gate · skip when a green push run covers the commit, Temporal Conformance (live PG + MySQL), UI Published Artifacts); 0 failure, 0 in progress; these ran on the merge with the main of that time, before #28235 landed). Author ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentScenario: an author of a registered action with
Verdict on ④: no must-fix; two should-fixes in ③. Implemented-by: VERDICT: PASS |
…tion-submit-label # Conflicts: # packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts # packages/spec/src/system/metadata-form-zod-reconciliation.test.ts # packages/spec/src/ui/action.zod.ts
|
domain:spec#4 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: PR #28188 (body, all ten comments, the file list of 21 files, the thirteen commits from the merge-base 3fd4d6b), the seat's hand-off 6537940517 naming this head and the three conflicts, my record 6537832125 (PASS on 528f8bf with the landing blocker: the head was dirty against ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Verdict reasoning. The hand-written lines of this PR are byte-identical to the ones record 6537832125 passed; each of the three conflicts keeps both sides, with the Implemented-by: VERDICT: PASS |
|
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN Serial-overlap note · 2026-10-31T05:04Z · from the ui seat, handing over the boundary flag of contract-review record 6538888897 on #28301. #28301 ( When it next moves:
The source hunks are on different keys, so only the count row should conflict. This is informational, and the ui seat does not push here. |
…tion-submit-label # Conflicts: # packages/spec/src/system/metadata-form-zod-reconciliation.test.ts
…erging main Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V New head · 2026-10-31T05:19Z · Local: spec Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429 "You've reached your Fable limit", 2026-10-30T04:26Z) · head 301f5b8 |
|
domain:spec#4 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
domain:spec#4 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: PR #28188 (body, all fifteen comments, the file list of 21 files, the fifteen commits from the merge-base b1e1dcf), the ui seat's serial-overlap note 6539355898 (#28301 merged as ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Verdict reasoning. The hand-written lines of this PR are byte-identical to the ones records 6537832125 and 6538202172 passed; the one conflict keeps both sides; the state-counts row is the sum the merged ledger implies; the two other regenerated files match Implemented-by: VERDICT: PASS |
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V Landing stroke · 2026-10-31T05:49Z · head
|
🤖 Generated with Claude Code
https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Fixes #27944
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up owed — skills/objectstack-ui/rules/actions.md, skills/objectstack-ui/evals/views-apps-actions-pages.json, skills/objectstack-i18n/SKILL.md
维护者速读
submitText:动作收集参数的对话框里提交按钮上的文字(可翻译,字符串或内联多语言映射)。翻译位在objects.OBJECT._actions.ACTION.submitText,translateAction会套用译文。没有params的动作写这个键会被拒绝(它根本不会打开参数对话框),空字符串也被拒绝;翻译包里给无参数动作写这个键,os validate报translation-target-unknown。submitTexton the submit button, falling back to Confirm #28158,本 PR 先落地。submitText而不是派发时提议的submitLabel,理由见下文 The name;submitLabel与confirmLabel作为别名改名到它。若你要submitLabel,改名是机械的一次提交。packages/spec/src/ui/action.zod.ts(新键、空白拒绝、无 params 拒绝、别名),packages/spec/src/system/i18n-resolver.ts(translateAction只在有 params 时套用译文),packages/lint/src/validate-translation-references.ts(checkActionSubmitText)。pnpm --filter @objectstack/spec exec vitest run src/ui/action-submit-text.test.ts,预期 18 个用例全部通过,其中一条断言无 params 的动作写submitText被拒、路径为submitText。submitText(或指定submitLabel),之后席位落地。What changes
ActionSchema.submitText(packages/spec/src/ui/action.zod.ts), an optional key declared besideparams. It is the label of the submit button in the dialog an action opens to collect its params ("Send", "Log Call"), in place of the dialog's default "Confirm".submitLabel; measured on549203d040, the protocol already has a word for a submit button's copy,object-form'ssubmitText(I18nLabelSchema, besidecancelText/nextText/prevText,component.zod.ts).submitLabelis a spelling the platform retired twice in favour of that one: theelement:formprop (18.ui__ElementFormProps__submitLabeltombstone) and the page component-copy translation key (translation-component-submit-label-removed, whose refusal tells the author "the live form surface's submit copy isobject-form'ssubmitText"). ReintroducingsubmitLabelas a live key here would teach the word those refusals point away from. It does not collide withconfirmText: that is the MESSAGE of the confirm dialog a param-LESS action opens; this is the BUTTON of the param dialog, and the two never meet on one registered action (see the refusals).NON_BLANK_STRINGrefinement (so the JSON Schema carriesminLength+ a\Spattern too), orInlineLocaleMapSchema. A blank string is refused, since it would replace "Confirm" with an empty button.params. The console's action runner opens the param dialog only for a non-emptyparamsarray, sosubmitTexton any other registered action would parse clean and never show: refused at pathsubmitText(ADR-0078's authoring-time rule, the mirror of the existingconfirmText-beside-paramsrefusal). Not picked ontoInlineActionSchema, which does not pickdescriptioneither.submitLabel(above) andconfirmLabel, the same button's spelling on a view'sbulkActionDefsdialog. Both rename ontosubmitTexton the action and in a translation bundle.actionTranslationSchemadeclaressubmitTextat both addresses (objects.OBJECT._actions.ACTION,globalActions.ACTION).translateActionoverlays it likedescription, but only for an action that declares a non-emptyparams, the wayresolveActionResultDialogreads nothing for an action with no dialog.validateTranslationReferencesgainscheckActionSubmitText: asubmitTexttranslation under an action that declares noparamsistranslation-target-unknownaterror(the result-dialog leaves' rule, withparamsas the declaration). The action-entry coverage pin classifies the new key asreference-checked, backed by a ghost at both addresses.submitTextisplanned+authorWarnuntil the console reads it (Action param dialog: draw the action'ssubmitTexton the submit button, falling back to Confirm #28158 flips it). The metadata form ledger records the key as anomitrow ("planned, not yet rendered", thepage.print/field.conditionalFormattingprecedent), so the Studio action form does not offer a control nothing draws; Action param dialog: draw the action'ssubmitTexton the submit button, falling back to Confirm #28158 deletes that row and adds the form row.ui/packages/core/src/actions/actionKeys.tslistssubmitTextinSPEC_ACTION_KEYS(its pin walks the spec'sActionSchema; without itTest UIreds).packages/metadata-protocol's/meta/typesaction key count pin moves 48 → 49 and its sample names the key.content/docs/protocol/objectui/actions.mdxgains "Naming the submit button:submitText" under Action Parameters and the property listing line;content/docs/ui/translations.mdx's action row names the new copy.@objectstack/specminor (both declaration lines),@objectstack/lintpatch,@object-ui/corepatch (Metadata-protocol: noneon both).The console half is #28158 (
domain:ui, labelfinding): both param-collection handlers resolve the key,ActionParamDialogdraws it withactionDialog.confirmas the fallback,UIActionSchemaderives it, the ledger row flips tolive, and the action form offers it. It lands after this PR.Independent review: none run (quota); the PR waits for the seat's contract-review hand-off.
Tests
packages/spec/src/ui/action-submit-text.test.ts(new, 18): string and locale-map parse; optional; non-I18nLabel shapes refused aslabelrefuses them; blank and whitespace refused (codecustom, pathsubmitText, message first sentence); refused withoutparamsand withparams: []; both aliases rename (unrecognized_keys,→ submitText); not on the inline action; translation slot at both addresses, alias inside a bundle;translateActionoverlays per locale, readsglobalActions, falls back to the authored label, resolves a bundle label for an action that authors none, and gives no button to an action without params.packages/lint/src/validate-translation-references.test.ts:submitTextunder param-less actions errors at both addresses (rule, severity, path, message first sentence); accepted under an action with params (control); coverage table entry + the all-keys real-instance control.packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts: count 49, sample namessubmitText.packages/spec/src/system/metadata-form-zod-reconciliation.test.ts:action/submitTextomit row.Acceptance notes
os i18n extract(packages/cli/src/utils/i18n-extract.ts) does not scaffoldsubmitTextyet;descriptionanddisabledReasonare scaffolded there. Left out becausepackages/cliis outside this card's claimed file surface; a bundle written by hand resolves today. The seat decides whether it rides Action param dialog: draw the action'ssubmitTexton the submit button, falling back to Confirm #28158 or its own card.bulkActionDefsdialog already has its own button label key,confirmLabel(a plainstring, not an I18nLabel); this PR does not touch that surface.Verification
Local readings, all on head
734502240e(branch merged withorigin/mainonce before opening;packages/specrebuilt after the merge).Targeted tests
@objectstack/spec:vitest run src/ui src/system: 229 files, 7942 tests, 1 failure, the metadata form reconciliation pin asking for thesubmitTextrow. Fixed by the omit row; re-run of that file plusaction-submit-text.test.ts: 2 files / 94 tests passed.@objectstack/spectypecheck: exit 0 (check:test-typecheck: OK).@objectstack/lint:validate-translation-references.test.ts+lint-liveness-properties.test.ts: 2 files / 332 tests passed.@objectstack/metadata-protocol(dependency closure built first):protocol.meta-types-degenerate-derivation.test.ts: 18 tests passed.ui/packages/core/src/actions/__tests__/actionKeys.pin.test.ts(from theui/root): 19 tests passed.pnpm --filter @objectstack/spec check:generated: exit 0 after--fixregeneratedcontent/docs/references/**(4 pages) and the build wroteauthorable-surface/ui.json;gen:liveness-countsmovedactionto planned 1 / classified 50.Derived gate families (dispatch-gates --commands, 109)
--ranreconciliation: 109 derived, 105 run, 4 NOT-MEASURED, 0 UNRUN. 104 exited 0.check:skill-examples,check:dual-build-cjs-loads,check:i18n-walk-parity,check:lean-entry-closure. CI builds and runs them.pnpm --dir ui exec vitest run scripts/: exit 1, 3 of 4903 tests. Two are 15 s timeouts on the shared box (check-hand-rolled-comment-maskprocess run,js-comment-masktype-check program); one isunit-registry-absence-collision, which could not import modules whose@objectstack/clientdist is not built here. None reads a file this diff changes; read as NOT MEASURED,Test UIin CI is the reading.Generated by Claude Code