Repository navigation
feat(core): an action's param dialog description, helpText and outcomeMessages fill {{ }} template holes, and still fill ${…} - #28436
Conversation
…ges, keep ${…}
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibwQ8tWwY7TiHQPMCHR
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibwQ8tWwY7TiHQPMCHR
|
triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: ① Derived judgments
② Rulings cited
③ Narrowing / widening
④ Behaviour / flags
Implemented-by: |
|
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D 维护者速读
席位核对 · ACCEPT ·
|
Requested by Tesla · project thread
Part of #27948
Clause-②: no
Metadata-protocol: none
维护者速读
description、每个参数的helpText、以及outcomeMessages现在也会填{{ record.x }}/{{ result.x }}这种模板洞(含{{ amount | currency:USD }}格式化),和confirmText/successMessage用同一个模板引擎;原来的${…}写法照旧能填。confirmText挪到description,可挪过去后{{ record.x }}原样显示成花括号;裁决 A(「同意」)定五个文案键统一成{{ }},渲染端先行。这是第 1 步。@object-ui/core的动作运行器;一句话里有{{ }}就只走模板引擎(同句里的${…}当普通文字),没有就走旧的${…},现有文案行为不变;回滚即 revert 本 PR。dialogCopyFiller、composeSuccessMessage第一档)、ui/packages/core/src/actions/actionCopyTemplate.ts(fillTransitionalCopy)、ui/packages/core/src/actions/tests/ActionRunner.transitionalCopy-27948.test.ts。cd ui && pnpm exec vitest run packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts,预期 15/15 通过。What changes
Step 1 of #27948, ruling 6545229148, letter A (renderer first). The five action copy keys become one ADR-0032
{{ }}template slot.confirmTextandsuccessMessagealready read it; the param dialog'sdescription, each param'shelpText, andoutcomeMessagesread only${…}. So an author following the spec (move the confirm question of an action with params fromconfirmTexttodescription) saw{{ record.x }}printed raw, from the record header and from a row.ui/packages/core/src/actions/actionCopyTemplate.ts:fillTransitionalCopy(text, fillTemplate, fillLegacy), one dialect per sentence, the ruleconfirmText/successMessagekept through their own transition (objectui#13367 until objectui#13476): a sentence with a{{ }}hole is filled by the template engine alone, so a${…}token in it stays text and a filled value is never read a second time; a sentence with none keeps its${…}reading.ui/packages/core/src/actions/ActionRunner.ts:interpolatehanded to the param-collection handler is nowdialogCopyFiller(action):{{ }}holes fill overrecord(the row on a row dispatch, the page's record on the header) and the action's staticparams, exactly the rootsconfirmTextreads (factored out ofconfirmMessageasrecordCopyRoots);${…}still goes throughtemplateEvaluatorFor(objectui#12967). Both handlers (useConsoleActionRuntime,RecordDetailView) already pass thatinterpolateto the dialog fordescriptionandhelpText, so no app-shell code moves.composeSuccessMessagerung 1 (outcomeMessages):{{ }}holes fill overresultandrecord, the rootssuccessMessagereads, with the viewer's locale and tenant currency;${result.*}still goes throughsubstituteScopeTokens.ActionDef.outcomeMessages,ParamCollectionHandler,RUNNER_TEXT,templateEvaluatorForandsubstituteScopeTokenssay which spelling each reads..changeset/27948-action-copy-template-holes.md:@object-ui/corepatch.No
packages/spec/**change: the describe text, the publish refusal of${…}and the migration of existing${…}copy are step 2, after this is onmain.Tests
ui/packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts, 15 pins through the real runner and@objectstack/formula'stemplateEngine, reading what the host is handed (the dialog'sinterpolateoutput and the toast handler's argument):description+helpText, on the header door and the row door (params._rowRecord, asObjectGriddispatches): the card's case (theconfirmTextsentencePause {{ record.display_name }}?reads the same moved todescription), a${record.x}hole still fills, an unknown{{ x }}hole reads exactly asconfirmTextreads it; a formatter hole; a mixed sentence ({{ }}wins,${…}stays text); a filled value is not re-read as${…}.outcomeMessages:{{ result.x }},{{ record.x }}from the row, a formatter hole,${result.x}still fills, an unknown{{ x }}reads assuccessMessagereads it, a mixed sentence.Reverse check against the unfixed runner (
ActionRunner.tsandactionCopyTemplate.tsrestored from5da3a8ce, test file kept): 12 failed, 3 passed. The 3 that stay green are the${…}keepers, which pin preservation and are meant to pass on both. Restore proven by an emptygit diff HEAD.Acceptance notes
{{ }}hole makes the whole sentence a template;${…}in it is text. This is the readingconfirmText/successMessagehad during their transition, so step 2's refusal of${…}changes nothing for a sentence that already has a{{ }}hole.recordand staticparams(not the collected values, which do not exist yet, the same asconfirmText);outcomeMessagesreadsresultandrecord. The${…}path keeps its wider scope (the whole runner context) until step 2 retires it; step 2's migration should check producers for${user.*}-style holes, which have no{{ }}root here.description/helpTextare localized in app-shell beforeinterpolate, andoutcomeMessagesentries are read only when they are strings. If step 2 types them as template slots that parse to the{ dialect, source }envelope, the readers must accept the envelope first (the trap objectui#13367 closed forconfirmText/successMessage), or a served entry silently falls back.${result.*}inoutcomeMessages/successMessage(platform-objects' invitation copy) are untouched;${…}keeps filling.Verification
Readings at head
164cbe99on objectstack-ai/objectstack (merge base5da3a8ce).Tests
cd ui && pnpm exec vitest run packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts: Tests 15/15 passed.cd ui && pnpm exec vitest run packages/core/: Test Files 224/224, Tests 4197 passed, 27 skipped.cd ui && pnpm exec vitest run packages/app-shell/src/views/ActionParamDialog packages/app-shell/src/views/ObjectView.inviteOutcome-27797 packages/app-shell/src/hooks/: Test Files 102/102, Tests 886/886.pnpm --filter @object-ui/core type-check(sources andtsconfig.test.json): lock VERDICT command-exit 0.Gates
env -u OS_SKIP_DTS pnpm turbo run build --filter='@object-ui/app-shell^...' --filter='!@object-ui/*'(37/37 tasks) and--filter='@object-ui/core^...'(3/3), lock VERDICT command-exit 0 both.dispatch-gates --commandsat164cbe99: 34 families, each run with stdin from/dev/null, exit codes recorded before any pipe; all 34 exit 0 (pnpm --dir ui exec vitest run scripts/: Test Files 169 passed, 2 skipped).--ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN.origin/mainmoved to630b4078after branch-off; none of the incoming files is this PR's andgit merge-treeagainst it is clean.test:repo, the UI lanes, lint) is CI's.Generated by Claude Code