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 }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
ActionRunner: confirmText / successMessage / errorMessage read the template envelope and fill {{ }} holes (the renderer half of objectstack#24814, ruling A) #13367
Filing gate ③ a follow-up owed by a ruling: the renderer-first half of objectstack-ai/objectstack#24814, ruling A (maintainer's pick on the spec seat's decision card, 2026-10-23). Filed by domain:spec seat 1 of objectstack (seat post objectstack#6017, session session_013odhA26dvzy7pQmevdTTvy). ⛔ Not a claim. Grade and routing are this repo's triage.
Why
objectstack#24814 types the action copy slots (confirmText, successMessage, errorMessage on the page component action rows, and the same keys on a registered ActionSchema) as template slots, the same mechanism objectstack#24798 used for the bulk confirmText (renderer half: objectstack-ai/objectstack#25942). The dev's measurement (objectstack#24814 os-dev-report 6365644870) found that the shared ActionRunner today:
fills ${…} holes in confirmText through ExpressionEvaluator, with the row bound as record;
fills ${result.*} in successMessage through substituteScopeTokens, and composeSuccessMessage skips any non-string value;
interpolates nothing in errorMessage, and falls back to the generic failure text for a non-string;
fills no {{ }} hole in any of the three.
So if the spec moves first, the served value becomes a template envelope, and the authored success and error toasts silently become the defaults in the shipping console. The ruling therefore orders the renderer first, as #24798 did.
Transition: keep today's ${…} reading working. The platform's own objects ship ${result.email}-style success sentences (sys-invitation, sys-member, sys-organization, sys-user, in objectstack packages/platform-objects), which the spec card converts in the same family. A reader that drops ${…} before that conversion lands breaks them.
Tests: a plain string, an envelope with each declared root, a ${…} sentence still filled, and a non-string never reaching a React child.
Then a .objectui-sha bump in objectstack, after which objectstack#24814 types the slots.
Order
Before objectstack#24814 (which carries Blocked-by: objectstack-ai/objectui#13367). Precedent and shared engine: objectstack-ai/objectstack#25942.
Dedupe
Open and closed issues here mentioning ActionRunner with successMessage / confirmText template: the only related card is objectstack-ai/objectstack#25942 (bulk confirmText, a different slot and envelope reader). New.
Claim: card thread · domain:ui seat 1 (seat post #5560, project run by the Sales account) · 2026-10-23T16:35Z
Session: session_0159TkE3rFbrKwMs3mWhnniW (a card thread of the project that runs domain:ui seat 1, dispatched by its coordinator; 由Sales账户的项目负责)
Account: os-sales (the seat's linked user; the card's assignee from this stroke)
Branch: claude/issue-13367-action-copy-template
Worktree: objectui-issue-13367
Domain: domain:ui
Seat: domain:ui#1
File surface: packages/core/src/actions/ActionRunner.ts (the three reads), a new copy-template helper beside it in packages/core/src/actions/ and its tests, packages/react/src/hooks/useActionTextLocalizer.ts (it must hand the envelope on, not collapse it), the tests of each, .changeset/ (stop on breach; explain in the report)
Container & model: M, mode:thread, model: default judgment tier (dispatch-gates --tier --repo objectstack-ai/objectui names no path-derived mandate for this surface), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: objectui#13395 (objectui#13384, this lane, same account) edits ActionRunner.ts in other hunks (a refusal inserted just above the confirm read, the RUNNER_TEXT docblock); whichever lands second merges main and re-runs. objectui#13394 (objectui#13379) does not touch these files. objectstack-ai/objectstack#25942 (baozhoutao, fill step not started) owns BulkActionDialog / bulkConfirmTextSource in plugin-grid; this card does not touch them.
Engine: the template engine objectstack-ai/objectstack#25942's ruling names ("the same template engine the flow notifications render with") is templateEngine in @objectstack/formula, already exported at this repo's .objectstack-sha pin (830a63a6) and already a dependency of @object-ui/core. This card calls it; it writes no engine of its own and no second hole grammar.
Plan, stated so the review can hold it to it:
One reader for the three keys: a string, or the { dialect?: 'template', source? } envelope (the shape bulkConfirmTextSource and formatTitleTemplate read). An envelope with no string source never reaches a React child.
confirmText: {{ }} holes filled over record (the dispatched row when there is one, else the context record, the scope templateEvaluatorFor already picks) and params (the action's static params object, when it has one). A sentence with no {{ }} hole keeps today's ${…} reading through ExpressionEvaluator.
successMessage: {{ }} holes filled over result (the payload ${result.*} reads) and record. A sentence with no {{ }} hole keeps today's ${result.*} substitution.
errorMessage: no root until one is ruled: the envelope's source (or the string) is shown verbatim.
One dialect per sentence: a sentence with a {{ }} hole goes through the template engine only, so a value filled into one hole is never read again as a ${…} expression.
The localizer keeps the envelope's shape and resolves its source the way it resolves a string today, so the console path reaches the runner with the envelope intact.
{
"issue": 13367,
"status": "done",
"branch": "claude/issue-13367-action-copy-template",
"pr": "https://github.com/objectstack-ai/objectui/pull/13409",
"session": "session_0159TkE3rFbrKwMs3mWhnniW",
"premise_still_valid": true,
"summary": "Head 4f05cae (f25bc24 + 9d7dd71 + a merge of origin/main 889d028). ActionRunner reads confirmText, successMessage and errorMessage as a string or a { dialect?: 'template', source? } envelope and fills {{ }} holes with @objectstack/formula's templateEngine (the flow notifications' engine; already in this repo's .objectstack-sha pin and a dependency of @object-ui/core, so objectstack-ai/objectstack#25942 was not waited on and no engine was written): confirmText over record (row or context record, the templateEvaluatorFor rule) and params (static params minus _rowRecord), successMessage over result and record, errorMessage verbatim. A sentence with no {{ }} hole keeps the ${...} reading (transition for the platform objects' ${result.email} copy); one dialect per sentence, so a filled value is never evaluated as ${...}. A sourceless envelope never reaches a React child; a sourceless confirmText envelope still gates with ''. useActionTextLocalizer kept the keys as I18nLabel maps and pickLocalized collapsed the envelope to the word 'template'; it now keeps the envelope and a bundle entry replaces source. File-surface breach, explained in the PR: app-shell flowResponse.flowSuccessCopy and FlowRunner's actionSuccessMessage type (type-check went red at the two flow launch sites after ActionDef widened; an envelope would otherwise lose to the flow's own copy). Clause-② corrected from the claim's no to yes in the PR and changeset (ActionDef widens, core exports ActionCopyTemplate, isActionCopyEnvelope, actionCopySource, hasTemplateHoles, fillTemplateHoles).",
"tests": "New: ActionRunner.copyTemplate-13367.test.ts (28), useActionTextLocalizer.templateEnvelope-13367.test.tsx (5), 3 cases in flowResponse.test.ts. Ablation on f25bc24 (localizer collapseCopy back to pickLocalized, success rung back to string-only): 9 failed (5 runner, 4 localizer); restored with git checkout HEAD -- packages, git diff --quiet HEAD clean. vitest --maxWorkers=2 packages/core/ packages/react/ -> 331 files, 5566 passed; components action renderers + app-shell hooks/utils + plugin-grid -> 391 files, 3995 passed; after the app-shell commit, app-shell hooks/utils/flow views -> 141 files, 1427 passed; after merging main, core actions + react hooks + flowResponse + objectstack-ai/objectui#13379 navigator pins -> 84 files, 1097 passed.",
"gates": "Local: NODE_OPTIONS=--max-old-space-size=8192 turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/console -> 37/37 (a first wider run was red at the two flow launch sites, fixed by 9d7dd71); eslint on touched files 0 errors; check:new-line-citations, control-bytes, spec-symbols, changeset-claims, pending-changeset-literals, unreferenced-sources, test-path-roots, action-forward-parity, handler-key-reads, phantom-deps, self-import, esm-specifiers, component-surface-parity, vi-mock-specifiers, comment-mask-corpus, check-changeset-presence exit 0; check:readme-exports needs a full build, left to CI. CI on 4f05cae: every check success (Type Check, Lint, Repo Gates, Test + shards 1-16, Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Changeset Bump Policy / Claim Re-read / Declaration / Fixed Group Check / Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Live E2E (informational), label); skipped: dependabot, Test (coverage), Test (coverage shard).",
"dogfood_battery": "n/a — objectui",
"mcp_calls": "github issue_read / pull_request_read / search_pull_requests (reads only); claude-code-remote add_repo, subscribe_pr_activity (session tools). No MCP write tool.",
"api_writes": "Relay via scripts/pm on objectstack-ai/objectui: label-write on objectstack-ai/objectui#13367 (pm:queue -> pm:dispatched, assignee os-sales), claim comment 6369803045 (post-stamped), pr_create objectstack-ai/objectui#13409 with assignee os-sales, pr_request_reviewers os-sales, this report (post-stamped); plus git push.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — template formatters render in en-US because the runner has no language; {{ amount | currency }} does not follow the user's locale",
"carrier: none · noted, not filed — FlowRunner's terminal toast after a paused flow shows the action's successMessage as written; neither {{ }} nor ${result.*} is filled there (unchanged by this PR)",
"carrier: none · noted, not filed — Studio ActionDefaultInspector / ActionPreview read the three keys as I18nLabel maps; an envelope would show as 'template' (authoring surface)",
"carrier: objectstack-ai/objectstack#25942 fill step — can call core fillTemplateHoles over its own roots; plugin-grid untouched here"
],
"deviations": "(1) Clause-② no in the claim, yes in the PR (see summary). (2) File surface grew by app-shell flowResponse.ts + FlowRunner.tsx (type-check red otherwise), explained in the PR body. (3) Head carries a merge of origin/main; the full local suites ran before it, a targeted set after it."
}
Activity
objectstack-fleet commentedon Oct 23, 2026
Claim: card thread ·
domain:uiseat 1 (seat post #5560, project run by the Sales account) · 2026-10-23T16:35ZSession:
session_0159TkE3rFbrKwMs3mWhnniW(a card thread of the project that runsdomain:uiseat 1, dispatched by its coordinator; 由Sales账户的项目负责)Account:
os-sales(the seat's linked user; the card's assignee from this stroke)Branch:
claude/issue-13367-action-copy-templateWorktree:
objectui-issue-13367Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/core/src/actions/ActionRunner.ts(the three reads), a new copy-template helper beside it inpackages/core/src/actions/and its tests,packages/react/src/hooks/useActionTextLocalizer.ts(it must hand the envelope on, not collapse it), the tests of each,.changeset/(stop on breach; explain in the report)Container & model:
M,mode:thread,model: default judgment tier (dispatch-gates --tier --repo objectstack-ai/objectui names no path-derived mandate for this surface),effort: highClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: objectui#13395 (objectui#13384, this lane, same account) edits
ActionRunner.tsin other hunks (a refusal inserted just above the confirm read, theRUNNER_TEXTdocblock); whichever lands second merges main and re-runs. objectui#13394 (objectui#13379) does not touch these files. objectstack-ai/objectstack#25942 (baozhoutao, fill step not started) ownsBulkActionDialog/bulkConfirmTextSourcein plugin-grid; this card does not touch them.Engine: the template engine objectstack-ai/objectstack#25942's ruling names ("the same template engine the flow notifications render with") is
templateEnginein@objectstack/formula, already exported at this repo's.objectstack-shapin (830a63a6) and already a dependency of@object-ui/core. This card calls it; it writes no engine of its own and no second hole grammar.Plan, stated so the review can hold it to it:
{ dialect?: 'template', source? }envelope (the shapebulkConfirmTextSourceandformatTitleTemplateread). An envelope with no stringsourcenever reaches a React child.confirmText:{{ }}holes filled overrecord(the dispatched row when there is one, else the context record, the scopetemplateEvaluatorForalready picks) andparams(the action's static params object, when it has one). A sentence with no{{ }}hole keeps today's${…}reading throughExpressionEvaluator.successMessage:{{ }}holes filled overresult(the payload${result.*}reads) andrecord. A sentence with no{{ }}hole keeps today's${result.*}substitution.errorMessage: no root until one is ruled: the envelope'ssource(or the string) is shown verbatim.{{ }}hole goes through the template engine only, so a value filled into one hole is never read again as a${…}expression.sourcethe way it resolves a string today, so the console path reaches the runner with the envelope intact.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{ "issue": 13367, "status": "done", "branch": "claude/issue-13367-action-copy-template", "pr": "https://github.com/objectstack-ai/objectui/pull/13409", "session": "session_0159TkE3rFbrKwMs3mWhnniW", "premise_still_valid": true, "summary": "Head 4f05cae (f25bc24 + 9d7dd71 + a merge of origin/main 889d028). ActionRunner reads confirmText, successMessage and errorMessage as a string or a { dialect?: 'template', source? } envelope and fills {{ }} holes with @objectstack/formula's templateEngine (the flow notifications' engine; already in this repo's .objectstack-sha pin and a dependency of @object-ui/core, so objectstack-ai/objectstack#25942 was not waited on and no engine was written): confirmText over record (row or context record, the templateEvaluatorFor rule) and params (static params minus _rowRecord), successMessage over result and record, errorMessage verbatim. A sentence with no {{ }} hole keeps the ${...} reading (transition for the platform objects' ${result.email} copy); one dialect per sentence, so a filled value is never evaluated as ${...}. A sourceless envelope never reaches a React child; a sourceless confirmText envelope still gates with ''. useActionTextLocalizer kept the keys as I18nLabel maps and pickLocalized collapsed the envelope to the word 'template'; it now keeps the envelope and a bundle entry replaces source. File-surface breach, explained in the PR: app-shell flowResponse.flowSuccessCopy and FlowRunner's actionSuccessMessage type (type-check went red at the two flow launch sites after ActionDef widened; an envelope would otherwise lose to the flow's own copy). Clause-② corrected from the claim's no to yes in the PR and changeset (ActionDef widens, core exports ActionCopyTemplate, isActionCopyEnvelope, actionCopySource, hasTemplateHoles, fillTemplateHoles).", "tests": "New: ActionRunner.copyTemplate-13367.test.ts (28), useActionTextLocalizer.templateEnvelope-13367.test.tsx (5), 3 cases in flowResponse.test.ts. Ablation on f25bc24 (localizer collapseCopy back to pickLocalized, success rung back to string-only): 9 failed (5 runner, 4 localizer); restored with git checkout HEAD -- packages, git diff --quiet HEAD clean. vitest --maxWorkers=2 packages/core/ packages/react/ -> 331 files, 5566 passed; components action renderers + app-shell hooks/utils + plugin-grid -> 391 files, 3995 passed; after the app-shell commit, app-shell hooks/utils/flow views -> 141 files, 1427 passed; after merging main, core actions + react hooks + flowResponse + objectstack-ai/objectui#13379 navigator pins -> 84 files, 1097 passed.", "gates": "Local: NODE_OPTIONS=--max-old-space-size=8192 turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/console -> 37/37 (a first wider run was red at the two flow launch sites, fixed by 9d7dd71); eslint on touched files 0 errors; check:new-line-citations, control-bytes, spec-symbols, changeset-claims, pending-changeset-literals, unreferenced-sources, test-path-roots, action-forward-parity, handler-key-reads, phantom-deps, self-import, esm-specifiers, component-surface-parity, vi-mock-specifiers, comment-mask-corpus, check-changeset-presence exit 0; check:readme-exports needs a full build, left to CI. CI on 4f05cae: every check success (Type Check, Lint, Repo Gates, Test + shards 1-16, Test (dist pins), Build & E2E, Build Docs, Bundle Analysis, Changeset Bump Policy / Claim Re-read / Declaration / Fixed Group Check / Overwrite Report, Governed Surface Queue Guard, Line Citation Gate, Live E2E (informational), label); skipped: dependabot, Test (coverage), Test (coverage shard).", "dogfood_battery": "n/a — objectui", "mcp_calls": "github issue_read / pull_request_read / search_pull_requests (reads only); claude-code-remote add_repo, subscribe_pr_activity (session tools). No MCP write tool.", "api_writes": "Relay via scripts/pm on objectstack-ai/objectui: label-write on objectstack-ai/objectui#13367 (pm:queue -> pm:dispatched, assignee os-sales), claim comment 6369803045 (post-stamped), pr_create objectstack-ai/objectui#13409 with assignee os-sales, pr_request_reviewers os-sales, this report (post-stamped); plus git push.", "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed — template formatters render in en-US because the runner has no language; {{ amount | currency }} does not follow the user's locale", "carrier: none · noted, not filed — FlowRunner's terminal toast after a paused flow shows the action's successMessage as written; neither {{ }} nor ${result.*} is filled there (unchanged by this PR)", "carrier: none · noted, not filed — Studio ActionDefaultInspector / ActionPreview read the three keys as I18nLabel maps; an envelope would show as 'template' (authoring surface)", "carrier: objectstack-ai/objectstack#25942 fill step — can call core fillTemplateHoles over its own roots; plugin-grid untouched here" ], "deviations": "(1) Clause-② no in the claim, yes in the PR (see summary). (2) File surface grew by app-shell flowResponse.ts + FlowRunner.tsx (type-check red otherwise), explained in the PR body. (3) Head carries a merge of origin/main; the full local suites ran before it, a targeted set after it." }Generated by Claude Code
feat(core,react,app-shell): action confirmText, successMessage and er…
objectstack-fleet commentedon Oct 23, 2026
LANDED
objectui#13367 由 PR objectui#13409 合入 main,合并提交
8e90779dcb825a519c0c002b72d6f10b1d56819f(2026-10-23T17:37Z,经合并队列)。记录于 2026-10-23T18:44Z。Seat: domain:ui#1
落地内容:动作的
confirmText、successMessage、errorMessage既可写字符串也可写template信封;{{ }}由@objectstack/formula的模板引擎按卡定的根填空,不带{{ }}的句子保留${…}写法。objectstack#24814 的 spec 一半可在.objectui-sha推进后给槽位定类型。合并时Fixes已关本卡。撤下
pm:dispatched。席位核对见 PR comment 6370631408。