Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
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

Closed

Description

@objectstack-fleet

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.

Scope

  • The runner reads both the plain string and the template envelope for the three keys, and fills {{ }} holes through the template engine list bulk action confirmText: fill the template slot's declared holes — {{count}} first, the action's params second (the renderer half of objectstack#24798 ruling B) objectstack#25942 uses. Roots: confirmText → record (and the action's params where the runner has them); successMessage → result and record; errorMessage → none until a scope is ruled (the text renders verbatim).
  • 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.


Generated by Claude Code

Activity

  1. added
    enhancementNew feature or request
    area:workflowApprovals and automation — the work that runs without a person driving it
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 23, 2026
  2. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    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.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  4. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    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。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:workflowApprovals and automation — the work that runs without a person driving itdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions