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 }}
Repository navigation
Action copy takes two hole syntaxes: description, param helpText and outcomeMessages read ${…}, confirmText and successMessage read {{ … }} #27948
Filing gate: ③ triage-directed. The order on #27434 said: triage rows 1–7, TAKE what is ui-only, CARD the rest. The card thread measured each row on origin/maine41642dd (report 6515174308, out_of_scope_findings).
Reach: any action whose copy carries a record or result hole. HotCRM knowledge article feedback actions (outcomeMessages), and send_email / log_call (description, param helpText).
Reader who acts: domain:spec first (one template slot, or a lint rule), then domain:ui (ActionRunner fill).
Duplicate search: search_issues "outcomeMessages successMessage hole syntax double brace dollar brace description helpText" → #24814 (closed; it typed confirmText / successMessage as template slots), nothing open.
Observed
confirmText and successMessage are ADR-0032 template slots: {{ … }} holes, and the publish gate refuses ${…}.
outcomeMessages is filled by substituteScopeTokens (${result.x} holes, composeSuccessMessage).
The param dialog's description and each param's helpText are filled through the runner's evaluator (${record.x}, templateEvaluatorFor).
No lint rule names the wrong hole on any of these keys.
Wanted
All five keys as one template slot: the spec describe, the publish refusal, the ui runner fill, and a migration for existing ${…} copy. At minimum, a lint rule naming the wrong hole on each key. The ui half follows as a domain:ui card once the spec decides the syntax.
Dedupe words: outcomeMessages hole syntax, description helpText template holes, double brace vs dollar brace action copy, successMessage outcomeMessages interpolation.
Ruling: batch 2026-10-31 item 4 · letter A · maintainer 「同意」 2026-10-31T15:13Z
The maintainer answered the consolidated decision list of 2026-10-31 (items 1–9, every item as recommended) with 「同意」 at 2026-10-31T15:10Z, in the project's decisions thread. Item 4 is this card.
A (ruled): all five keys (description, param helpText, outcomeMessages, confirmText, successMessage) become ADR-0032 {{ }} template slots. Renderer first: the console fills {{ }} on the three keys that read ${…} today and keeps reading ${…} through the transition; then the spec closes (describe text, publish refusal of ${…}) and a migration rewrites existing ${…} copy.
B (not taken): one lint rule that names the wrong hole, no contract change.
Work for A: two steps across spec and ui. Step 1 (renderer, both syntaxes accepted) is reversible: the transition can stop halfway with both read. Step 2 (spec narrowing + publish refusal + migration) follows once step 1 is on main.
Claim: PM loop round 5
Session: session_01L9qibx5r5nhDMwpRs4SL6D
Account: os-tesla
Branch: claude/project-thread-2br7jq (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27948
Domain: domain:spec
Seat: domain:spec#2
File surface: step 1 of 2 (renderer first): ui/packages/core/src/actions/**, ui/packages/app-shell/src/views/ActionParamDialog* and the helpers they call, their tests, a @object-ui/* patch changeset; PR says Part of #27948. ⛔ no packages/spec/** in this step, no governed path (stop on breach; explain in the report)
Container & model: S, mode:thread, model: TIER_DEFAULT, effort: high; landing: seat (not governed); contract review decided on the diff
Clause-②: no (step 1 moves no spec accept set; the console reads one more hole spelling, ${…} keeps working)
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6545229148
Premise: holds (re-read at origin/main5da3a8ce; the gap named on the card is still there)
Serial constraints cleared: none (no open PR touches ActionRunner copy fill or ActionParamDialog at claim time)
Ruling-ref: maintainer 「同意」 (2026-10-31T15:10Z), ruling 6545229148, letter A, renderer first; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); order: /mnt/project-files/spec-seat-2/r2026-10-30/dispatch-27948.md
Dispatched 2026-10-31T15:20Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
Draft PR #28436 at head 164cbe99 (step 1 of 2, renderer first; Part of #27948).
{
"issue": 27948,
"status": "done",
"branch": "claude/project-thread-2br7jq",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28436",
"session": "session_01L9qibwQ8tWwY7TiHQPMCHR",
"premise_still_valid": true,
"summary": "Step 1 of ruling A. ActionRunner now fills ADR-0032 double-brace holes in the param dialog description, each param helpText (the interpolate handed to the param-collection handler, now dialogCopyFiller) and an outcomeMessages entry (composeSuccessMessage rung 1), through the same templateEngine confirmText / successMessage use, with locale and tenant currency. Roots: dialog copy = record (row on a row dispatch) + static params, as confirmText; outcomeMessages = result + record, as successMessage. Dollar-brace keeps working. One dialect per sentence (fillTransitionalCopy in actionCopyTemplate.ts): a sentence with a double-brace hole goes to the template engine alone and any dollar-brace token in it stays text; a sentence with none keeps the dollar-brace reading. No app-shell code moved: both handlers already pass interpolate to the dialog. Premise re-measured at 5da3a8ce: the card's case printed raw on both doors.",
"tests": "New ui/packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts: 15/15 (header door + row door for description/helpText; outcomeMessages; formatter; unknown hole equals confirmText / successMessage reading; mixed sentence; no second read). Reverse check with ActionRunner.ts + actionCopyTemplate.ts restored from 5da3a8ce: 12 failed, 3 passed (the 3 dollar-brace keepers, meant to pass on both); restored, git diff HEAD empty. ui vitest packages/core/: 224/224 files, 4197 passed, 27 skipped. ui vitest app-shell ActionParamDialog* + ObjectView.inviteOutcome-27797 + src/hooks/: 102/102 files, 886/886. pnpm --filter @object-ui/core type-check (src + tsconfig.test.json): lock VERDICT command-exit 0.",
"gates": "dispatch-gates --commands at 164cbe99: 34 families, stdin from /dev/null, exit codes recorded before any pipe (record: /mnt/project-files/spec-seat-2/r2026-10-30/out/27948-gates-ran-164cbe99.list). --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0. origin/main 630b4078: no incoming file overlaps, merge-tree clean. CI check-runs on #28436: in_progress at report time.",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "+276 / -34 across 4 files vs merge base 5da3a8ce",
"files_changed": [
".changeset/27948-action-copy-template-holes.md",
"ui/packages/core/src/actions/ActionRunner.ts",
"ui/packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts",
"ui/packages/core/src/actions/actionCopyTemplate.ts"
],
"deviations": [
"None against the file surface: no packages/spec/**, no governed path, no app-shell file (the dialog already routes description/helpText through the runner's interpolate).",
"The harness asked to set the PR assignee and reviewer after pr_create; not done, per FRAME (the seat lands, no assign, no review request, no MCP writes)."
],
"mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2) on #27948; 0 writes",
"api_writes": "2 — relay pr_create (fleet-write run 41217473552, PR #28436, 7991 bytes sent = stored); this os-dev-report comment via post-stamped",
"open_questions": [],
"out_of_scope_findings": [
"carrier: step 2 of #27948 · the three keys are read as strings only: description/helpText are localized in app-shell before interpolate and outcomeMessages entries 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 take the envelope first (the trap objectui#13367 closed for confirmText/successMessage) or a served entry silently falls back · dedupe words: outcomeMessages template envelope, description helpText envelope",
"carrier: step 2 of #27948 · the dollar-brace path reads the whole runner context (user, ctx…), the double-brace roots are record/params and result/record only; the migration should look for dollar-brace holes outside those roots before rewriting · dedupe words: action copy migration roots, dollar-brace user hole"
]
}
Claim: PM loop round 5
Session: session_01L9qibx5r5nhDMwpRs4SL6D
Account: os-tesla
Branch: claude/project-thread-2br7jq (the card thread's designated push branch, the recorded platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-27948
Domain: domain:spec
Seat: domain:spec#2
File surface: step 2 of 2 (restarts the branch from main after step 1 #28436 merged): packages/spec/src/ui/action.zod.ts + tests, the publish refusal, packages/spec/src/migrations/**, in-repo producers on the five keys, the tables the gates force, ui/packages/core/src/actions/** (envelope readers only), changesets; PR says Fixes #27948. ⛔ no skills/**, no docs/adr/**, no other ui/** (stop on breach; explain in the report)
Container & model: S, mode:thread, model: TIER_DEFAULT, effort: high; landing: seat after the lane contract review; Tier H only if the diff hits a governed path (it must not)
Clause-②: yes (narrowing: ${…} refused at publish on description, param helpText, outcomeMessages; title carries !)
Metadata-protocol: changed
Responsibility: n/a — not a defect card
Thread-read: 6545766362
Premise: holds (re-read at origin/mainf91ab5a0; the gap named on the card is still there)
Serial constraints cleared: step 1 #28436 merged (f91ab5a); no open PR touches action.zod.ts copy keys at claim time
Ruling-ref: maintainer 「同意」 (2026-10-31T15:10Z), ruling 6545229148, letter A, step 2; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); order: /mnt/project-files/spec-seat-2/r2026-10-30/dispatch-27948-step2.md
Dispatched 2026-10-31T16:44Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
File surface: + the three envelope readers outside ui/packages/core/src/actions/** that the step-2 typing breaks (measured on dist at f91ab5a0, card thread stop report out/27948-step2-surface-stop.md): @object-ui/reactuseActionTextLocalizer / pickLocalized (an outcomeMessages envelope collapses to "template"), the runtime summarizeActionParams (an AI tool param description reads "[object Object]"), and the cli i18n-extract (the three keys read as a locale map). Each is a mechanical envelope-accepting fix, pinned, in the PR's first commit, before the spec typing commit. ⛔ Still no skills/**, no docs/adr/**.
Metadata-protocol: narrowed (was written changed in the claim; narrowed is the legal value; minor + ADR-0087 marker per the lane rule).
File surface: + a fourth envelope reader the step-2 typing breaks: ui/packages/app-shell/src/views/metadata-admin/previews/ActionPreview.tsx (Studio's action preview; it draws a param's helpText raw, so a parsed envelope becomes an object as a React child, and its localize() reads a confirmText / successMessage envelope as a locale map). Fix: localize() reads the envelope source through core's actionCopySource, helpText goes through localize(); plus its pin ActionPreview.copyEnvelope-27948.test.tsx. In the PR's first commit with the other readers. ⛔ Still no skills/**, no docs/adr/**.
File surface: + a fifth envelope reader, forced by CI "Type Check UI" on PR #28463: @object-ui/fieldsresolveActionParams.ts (RawActionParam.helpText no longer accepted the spec's ActionParam once helpText became a template slot). Fix at 408466db: helpText typed unknown, every emit reads it through actionCopySource, a field-backed param whose envelope carries no text falls back to the field's help; pin resolveActionParams.helpTextEnvelope-27948.test.ts; @object-ui/fields patch in the readers changeset. A separate commit after the push (no force-push); listed as a deviation in the dev report. ⛔ Still no skills/**, no docs/adr/**.
Draft PR #28463 at head e68dae86 (step 2 of 2; Fixes #27948).
{
"issue": 27948,
"status": "done",
"branch": "claude/project-thread-2br7jq",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28463",
"session": "session_01L9qibwQ8tWwY7TiHQPMCHR",
"premise_still_valid": true,
"summary": "Step 2 of ruling A, in five commits. (1) fcd2e654, readers first: ActionRunner dialogCopyOf/composeSuccessMessage, react useActionTextLocalizer, runtime summarizeActionParams, cli i18n-extract and app-shell ActionPreview take the template envelope on description, helpText and outcomeMessages. (2) 45d5e284: spec types ActionSchema.description and each outcomeMessages entry as actionCopy template slots with the locale map, and ActionParamSchema.helpText as actionParamHelpText() with no map. The lint door actionCopyTemplateIssues judges all three, so ${…} and the rest of the family's refusals are errors and the publish gate answers 422. Expression-conformance ledger rows, liveness evidence and reference docs follow. (3) a8f9f1c7: semantic migration entry action-dialog-outcome-copy-template-holes, with no conversion family. In-repo producers are rewritten to {{ }}: invite/resend outcomes, sys_invitation and sys_member descriptions, both permission_set_granted outcomes, four objects.generated bundles, the action form help and actions.mdx. (4) 408466db: @object-ui/fields resolveActionParams reads helpText through actionCopySource. CI Type Check UI named it, and the seat accepted it as the fifth reader under amendment 6547728288. The console keeps reading ${…} for sentences with no {{ }} hole. (5) e68dae86: the cli extractor pin and two plugin-approvals pins read a parsed description as the envelope (CI Test Core 5/8 and 6/8 on 408466db), and the outcomeMessages TSDoc plus the action-copy-template module note now say the stored-copy rewrite is by hand, naming the semantic entry (triage should-fix).",
"tests": "New pins, each reverse-checked: core ActionRunner.copyEnvelope-27948 4 (reverse 2F/2P controls); react useActionTextLocalizer.outcomeEnvelope-27948 3 (2F/1P); runtime action-param-help-envelope-27948 2 (2F); cli i18n-extract-action-copy-envelope-27948 2 (1F/1P); app-shell ActionPreview.copyEnvelope-27948 2 (1F/1P); app-shell resolveActionParams.helpTextEnvelope-27948 3 (2F/1P type pin); lint validate-action-copy-templates 6 new (5 refusal pins failed on reverse); spec action-copy-template 7 new. Suites: spec 773 files / 22871 tests; lint 183/183 files, 7699 tests; platform-objects 104/104, 1474; cli test/i18n 44/44, 419; runtime src/action* 29/29, 521; ui core actions + react hooks/context 108/108, 1263; app-shell metadata-admin previews 157/157, 1713; app-shell src/utils resolveActionParams* 6 files, 77. Type-check: spec, runtime, cli, @object-ui/core and @object-ui/react clean. Fields tsc shows no error in resolveActionParams.ts; the rest of its local errors are unbuilt-dist TS2307 in other files. check-i18n-bundles is in sync. At e68dae86: plugin-approvals 106/106 files, 1366 tests (3 pins fixed: approve-decision-question x2, sys-approval-request.object x1); platform-objects 104/104, 1474; plugin-auth 206/206, 3933; spec src/ui+shared+studio+migrations+source-reading pins 218/218, 7316; cli i18n 44/44 + i18n-extract-action-description 7/7. A whole-cli local run did not finish, and CI ran cli in its shards.",
"gates": "dispatch-gates --commands at a8f9f1c7: 116 families, stdin from /dev/null, exit codes recorded before any pipe, all exit 0. --ran: 116 derived, 116 run, 0 NOT-MEASURED. Record: /mnt/project-files/spec-seat-2/r2026-10-30/out/27948-step2-gates-ran-a8f9f1c7.list. 408466db (fields reader) and e68dae86 (test pins + TSDoc) are covered by CI. merge-tree against origin/main is clean. CI on e68dae86: every check-run green (65: Test Core 8/8 + rollup, Test UI 16/16 + rollup, Type Check UI, TypeScript Type Check, Lint & Repo Gates, Check Changeset, Spec property liveness, docs, Build Core/Docs/UI, Live E2E UI); opt-in and dogfood-shard jobs skipped by filter.",
"dogfood_battery": "Full dogfood (packages/qa/dogfood pnpm test, under os-verify-lock) at a8f9f1c7: exit 0, Test Files 362 passed / 3 skipped (365), Tests 2922 passed / 28 skipped. 408466db and e68dae86 touch nothing dogfood loads (a ui fields file, tests, TSDoc, a changeset line).",
"line_budget": "+1360 / -211 across 57 files vs merge base 776c1d87",
"files_changed": [
".changeset/27948-action-copy-envelope-readers.md",
".changeset/27948-action-copy-template-slots.md",
"content/docs/references/api/metadata.mdx",
"content/docs/references/api/package-api-assembled.mdx",
"content/docs/references/data/object.mdx",
"content/docs/references/kernel/metadata-plugin.mdx",
"content/docs/references/system/migration.mdx",
"content/docs/references/ui/action.mdx",
"content/docs/ui/actions.mdx",
"packages/cli/src/utils/i18n-extract.ts",
"packages/cli/test/i18n-extract-action-copy-envelope-27948.test.ts",
"packages/cli/test/i18n-extract-action-description.test.ts",
"packages/lint/src/validate-action-copy-templates.test.ts",
"packages/lint/src/validate-action-copy-templates.ts",
"packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/en.objects.generated.ts",
"packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts",
"packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts",
"packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts",
"packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts",
"packages/platform-objects/src/identity/action-confirm-one-dialog.test.ts",
"packages/platform-objects/src/identity/invitation-delivery-copy.test.ts",
"packages/platform-objects/src/identity/invitation-screens-copy.test.ts",
"packages/platform-objects/src/identity/invite-outcome-messages.ts",
"packages/platform-objects/src/identity/invite-user-action-copy.test.ts",
"packages/platform-objects/src/identity/oauth-register-form-types.test.ts",
"packages/platform-objects/src/identity/rotate-client-secret-copy.test.ts",
"packages/platform-objects/src/identity/sys-api-key-create-expiry.test.ts",
"packages/platform-objects/src/identity/sys-invitation.object.ts",
"packages/platform-objects/src/identity/sys-member.object.ts",
"packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts",
"packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts",
"packages/plugins/plugin-approvals/src/translations/approve-decision-question.test.ts",
"packages/qa/dogfood/test/expression-conformance.ledger.ts",
"packages/runtime/src/action-execution.ts",
"packages/runtime/src/action-param-help-envelope-27948.test.ts",
"packages/spec/liveness/action.json",
"packages/spec/src/migrations/entries/semantic/18.action-dialog-outcome-copy-template-holes.ts",
"packages/spec/src/system/i18n-resolver.ts",
"packages/spec/src/ui/action-copy-template.test.ts",
"packages/spec/src/ui/action-copy-template.ts",
"packages/spec/src/ui/action-description.test.ts",
"packages/spec/src/ui/action-outcome-messages.test.ts",
"packages/spec/src/ui/action.form.ts",
"packages/spec/src/ui/action.zod.ts",
"packages/spec/src/ui/component.zod.ts",
"ui/packages/app-shell/src/utils/resolveActionParams.helpTextEnvelope-27948.test.ts",
"ui/packages/app-shell/src/views/metadata-admin/previews/ActionPreview.tsx",
"ui/packages/app-shell/src/views/metadata-admin/previews/__tests__/ActionPreview.copyEnvelope-27948.test.tsx",
"ui/packages/core/src/actions/ActionRunner.ts",
"ui/packages/core/src/actions/__tests__/ActionRunner.copyEnvelope-27948.test.ts",
"ui/packages/core/src/actions/actionCopyTemplate.ts",
"ui/packages/fields/src/resolveActionParams.ts",
"ui/packages/react/src/hooks/__tests__/useActionTextLocalizer.outcomeEnvelope-27948.test.tsx",
"ui/packages/react/src/hooks/useActionTextLocalizer.ts"
],
"deviations": [
"Surface additions beyond the original claim, both seat-ruled: app-shell ActionPreview.tsx + pin (amendment 6546830184, in commit 1), and @object-ui/fields resolveActionParams.ts + pin (amendment 6547728288, in a separate 4th commit because the branch was already pushed; no force push).",
"Left as is by seat ruling: ui/packages/components/src/renderers/action/action-icon.tsx lines 319/332 tooltip `schema.label || schema.description`. It is unreachable from a parsed slot, since the localizer always fills label and a row's description is not a slot, and UIActionSchema still types description as a string.",
"Test pins outside the reader sweep (non-test sources) went red in CI on 408466db: cli i18n-extract-action-description.test.ts and plugin-approvals sys-approval-request.object.test.ts + approve-decision-question.test.ts. Fixed in e68dae86 at the seat's order; local plugin-approvals measured 3 failures where the lane named 1.",
"The PR body Acceptance notes were patched by the seat (issue_patch with its signature) from out/27948-step2-pr-body-v2.md; a card thread cannot sign the body.",
"The harness's post-create assignee and reviewer steps were not done, per FRAME (the seat lands; no assign, no review request, no MCP writes)."
],
"mcp_calls": "Reads only: mcp__github__pull_request_read (get, get_check_runs), mcp__github__get_job_logs and get_check_run (Type Check UI, the Test UI rollup on a8f9f1c7 that was cancelled by the next push, the Test Core 6/8 and planner logs on 408466db), mcp__github__issue_read on #27948. 0 writes.",
"api_writes": "2: relay pr_create (fleet-write run 41230166641, PR #28463); this os-dev-report comment via post-stamped. Plus git pushes of claude/project-thread-2br7jq (a8f9f1c7, 408466db, e68dae86). The PR body patch was the seat's issue_patch.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: #28464 (skills card, filed by the seat) · skills/objectstack-ui/rules/actions.md line 23 lists {{ }} copy holes for confirmText/successMessage/errorMessage only · dedupe words: skills action copy template holes description helpText outcomeMessages",
"carrier: objectstack-ai/hotcrm#2384 (filed by the seat, Blocked-by #27948) · knowledge_article.actions.ts lines 121-123/144-146 outcomeMessages ${result.helpful}/${result.notHelpful} plus the four objects.service.ts translations convert to {{ result.* }} at the pin bump · dedupe words: hotcrm outcomeMessages dollar-brace",
"carrier: none (comment only) · packages/plugins/plugin-auth/src/role-change-granted-set.ts line 29 comment still spells ${result.grantedPermissionSet.label}; outside the surface · dedupe words: role-change granted set comment dollar-brace"
]
}
Activity
objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Ruling: batch 2026-10-31 item 4 · letter A · maintainer 「同意」 2026-10-31T15:13Z
The maintainer answered the consolidated decision list of 2026-10-31 (items 1–9, every item as recommended) with 「同意」 at 2026-10-31T15:10Z, in the project's decisions thread. Item 4 is this card.
description, paramhelpText,outcomeMessages,confirmText,successMessage) become ADR-0032{{ }}template slots. Renderer first: the console fills{{ }}on the three keys that read${…}today and keeps reading${…}through the transition; then the spec closes (describe text, publish refusal of${…}) and a migration rewrites existing${…}copy.Work for A: two steps across spec and ui. Step 1 (renderer, both syntaxes accepted) is reversible: the transition can stop halfway with both read. Step 2 (spec narrowing + publish refusal + migration) follows once step 1 is on
main.objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Claim: PM loop round 5
Session:
session_01L9qibx5r5nhDMwpRs4SL6DAccount:
os-teslaBranch:
claude/project-thread-2br7jq(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27948Domain:
domain:specSeat:
domain:spec#2File surface: step 1 of 2 (renderer first):
ui/packages/core/src/actions/**,ui/packages/app-shell/src/views/ActionParamDialog*and the helpers they call, their tests, a@object-ui/*patch changeset; PR saysPart of #27948. ⛔ nopackages/spec/**in this step, no governed path (stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT,effort: high; landing: seat (not governed); contract review decided on the diffClause-②: no (step 1 moves no spec accept set; the console reads one more hole spelling,
${…}keeps working)Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6545229148
Premise: holds (re-read at
origin/main5da3a8ce; the gap named on the card is still there)Serial constraints cleared: none (no open PR touches
ActionRunnercopy fill orActionParamDialogat claim time)Ruling-ref: maintainer 「同意」 (2026-10-31T15:10Z), ruling 6545229148, letter A, renderer first; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); order: /mnt/project-files/spec-seat-2/r2026-10-30/dispatch-27948.md
Dispatched 2026-10-31T15:20Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibwQ8tWwY7TiHQPMCHR
os-dev-report
Draft PR #28436 at head
164cbe99(step 1 of 2, renderer first;Part of #27948).{ "issue": 27948, "status": "done", "branch": "claude/project-thread-2br7jq", "pr": "https://github.com/objectstack-ai/objectstack/pull/28436", "session": "session_01L9qibwQ8tWwY7TiHQPMCHR", "premise_still_valid": true, "summary": "Step 1 of ruling A. ActionRunner now fills ADR-0032 double-brace holes in the param dialog description, each param helpText (the interpolate handed to the param-collection handler, now dialogCopyFiller) and an outcomeMessages entry (composeSuccessMessage rung 1), through the same templateEngine confirmText / successMessage use, with locale and tenant currency. Roots: dialog copy = record (row on a row dispatch) + static params, as confirmText; outcomeMessages = result + record, as successMessage. Dollar-brace keeps working. One dialect per sentence (fillTransitionalCopy in actionCopyTemplate.ts): a sentence with a double-brace hole goes to the template engine alone and any dollar-brace token in it stays text; a sentence with none keeps the dollar-brace reading. No app-shell code moved: both handlers already pass interpolate to the dialog. Premise re-measured at 5da3a8ce: the card's case printed raw on both doors.", "tests": "New ui/packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts: 15/15 (header door + row door for description/helpText; outcomeMessages; formatter; unknown hole equals confirmText / successMessage reading; mixed sentence; no second read). Reverse check with ActionRunner.ts + actionCopyTemplate.ts restored from 5da3a8ce: 12 failed, 3 passed (the 3 dollar-brace keepers, meant to pass on both); restored, git diff HEAD empty. ui vitest packages/core/: 224/224 files, 4197 passed, 27 skipped. ui vitest app-shell ActionParamDialog* + ObjectView.inviteOutcome-27797 + src/hooks/: 102/102 files, 886/886. pnpm --filter @object-ui/core type-check (src + tsconfig.test.json): lock VERDICT command-exit 0.", "gates": "dispatch-gates --commands at 164cbe99: 34 families, stdin from /dev/null, exit codes recorded before any pipe (record: /mnt/project-files/spec-seat-2/r2026-10-30/out/27948-gates-ran-164cbe99.list). --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN, all exit 0. origin/main 630b4078: no incoming file overlaps, merge-tree clean. CI check-runs on #28436: in_progress at report time.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "+276 / -34 across 4 files vs merge base 5da3a8ce", "files_changed": [ ".changeset/27948-action-copy-template-holes.md", "ui/packages/core/src/actions/ActionRunner.ts", "ui/packages/core/src/actions/__tests__/ActionRunner.transitionalCopy-27948.test.ts", "ui/packages/core/src/actions/actionCopyTemplate.ts" ], "deviations": [ "None against the file surface: no packages/spec/**, no governed path, no app-shell file (the dialog already routes description/helpText through the runner's interpolate).", "The harness asked to set the PR assignee and reviewer after pr_create; not done, per FRAME (the seat lands, no assign, no review request, no MCP writes)." ], "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2) on #27948; 0 writes", "api_writes": "2 — relay pr_create (fleet-write run 41217473552, PR #28436, 7991 bytes sent = stored); this os-dev-report comment via post-stamped", "open_questions": [], "out_of_scope_findings": [ "carrier: step 2 of #27948 · the three keys are read as strings only: description/helpText are localized in app-shell before interpolate and outcomeMessages entries 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 take the envelope first (the trap objectui#13367 closed for confirmText/successMessage) or a served entry silently falls back · dedupe words: outcomeMessages template envelope, description helpText envelope", "carrier: step 2 of #27948 · the dollar-brace path reads the whole runner context (user, ctx…), the double-brace roots are record/params and result/record only; the migration should look for dollar-brace holes outside those roots before rewriting · dedupe words: action copy migration roots, dollar-brace user hole" ] }objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Claim: PM loop round 5
Session:
session_01L9qibx5r5nhDMwpRs4SL6DAccount:
os-teslaBranch:
claude/project-thread-2br7jq(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-27948Domain:
domain:specSeat:
domain:spec#2File surface: step 2 of 2 (restarts the branch from
mainafter step 1 #28436 merged):packages/spec/src/ui/action.zod.ts+ tests, the publish refusal,packages/spec/src/migrations/**, in-repo producers on the five keys, the tables the gates force,ui/packages/core/src/actions/**(envelope readers only), changesets; PR saysFixes #27948. ⛔ noskills/**, nodocs/adr/**, no otherui/**(stop on breach; explain in the report)Container & model:
S,mode:thread,model: TIER_DEFAULT,effort: high; landing: seat after the lane contract review; Tier H only if the diff hits a governed path (it must not)Clause-②: yes (narrowing:
${…}refused at publish ondescription, paramhelpText,outcomeMessages; title carries!)Metadata-protocol: changed
Responsibility: n/a — not a defect card
Thread-read: 6545766362
Premise: holds (re-read at
origin/mainf91ab5a0; the gap named on the card is still there)Serial constraints cleared: step 1 #28436 merged (f91ab5a); no open PR touches
action.zod.tscopy keys at claim timeRuling-ref: maintainer 「同意」 (2026-10-31T15:10Z), ruling 6545229148, letter A, step 2; dispatch authority = the maintainer's 「p3 也可以开发」 (2026-10-30T12:41Z) and 「每个座位并发可以5」 (2026-10-30T15:04Z); order: /mnt/project-files/spec-seat-2/r2026-10-30/dispatch-27948-step2.md
Dispatched 2026-10-31T16:44Z to a card thread of the seat's project; the order lives in the project. ⛔ The thread posts no claim and writes no assignee.
objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Amends claim 6546254270 · 2026-10-31T16:49Z
File surface: + the three envelope readers outside
ui/packages/core/src/actions/**that the step-2 typing breaks (measured on dist atf91ab5a0, card thread stop reportout/27948-step2-surface-stop.md):@object-ui/reactuseActionTextLocalizer/pickLocalized(anoutcomeMessagesenvelope collapses to "template"), the runtimesummarizeActionParams(an AI tool param description reads "[object Object]"), and the clii18n-extract(the three keys read as a locale map). Each is a mechanical envelope-accepting fix, pinned, in the PR's first commit, before the spec typing commit. ⛔ Still noskills/**, nodocs/adr/**.Metadata-protocol: narrowed (was written
changedin the claim;narrowedis the legal value; minor + ADR-0087 marker per the lane rule).objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Amends claim 6546254270 (after amendment 6546313079) · 2026-10-31T17:35Z
File surface: + a fourth envelope reader the step-2 typing breaks:
ui/packages/app-shell/src/views/metadata-admin/previews/ActionPreview.tsx(Studio's action preview; it draws a param'shelpTextraw, so a parsed envelope becomes an object as a React child, and itslocalize()reads aconfirmText/successMessageenvelope as a locale map). Fix:localize()reads the envelope source through core'sactionCopySource,helpTextgoes throughlocalize(); plus its pinActionPreview.copyEnvelope-27948.test.tsx. In the PR's first commit with the other readers. ⛔ Still noskills/**, nodocs/adr/**.fix(fields): resolveActionParams reads a template-envelope helpText (#…
objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D
Amends claim 6546254270 (after 6546313079, 6546830184) · 2026-10-31T18:54Z
File surface: + a fifth envelope reader, forced by CI "Type Check UI" on PR #28463:
@object-ui/fieldsresolveActionParams.ts(RawActionParam.helpTextno longer accepted the spec'sActionParamoncehelpTextbecame a template slot). Fix at408466db:helpTexttypedunknown, every emit reads it throughactionCopySource, a field-backed param whose envelope carries no text falls back to the field's help; pinresolveActionParams.helpTextEnvelope-27948.test.ts;@object-ui/fieldspatch in the readers changeset. A separate commit after the push (no force-push); listed as a deviation in the dev report. ⛔ Still noskills/**, nodocs/adr/**.test(cli,plugin-approvals): read a parsed action description as the t…
objectstack-fleet commentedon Oct 31, 2026
domain:spec#2 · os-tesla · session_01L9qibwQ8tWwY7TiHQPMCHR
os-dev-report
Draft PR #28463 at head
e68dae86(step 2 of 2;Fixes #27948).{ "issue": 27948, "status": "done", "branch": "claude/project-thread-2br7jq", "pr": "https://github.com/objectstack-ai/objectstack/pull/28463", "session": "session_01L9qibwQ8tWwY7TiHQPMCHR", "premise_still_valid": true, "summary": "Step 2 of ruling A, in five commits. (1) fcd2e654, readers first: ActionRunner dialogCopyOf/composeSuccessMessage, react useActionTextLocalizer, runtime summarizeActionParams, cli i18n-extract and app-shell ActionPreview take the template envelope on description, helpText and outcomeMessages. (2) 45d5e284: spec types ActionSchema.description and each outcomeMessages entry as actionCopy template slots with the locale map, and ActionParamSchema.helpText as actionParamHelpText() with no map. The lint door actionCopyTemplateIssues judges all three, so ${…} and the rest of the family's refusals are errors and the publish gate answers 422. Expression-conformance ledger rows, liveness evidence and reference docs follow. (3) a8f9f1c7: semantic migration entry action-dialog-outcome-copy-template-holes, with no conversion family. In-repo producers are rewritten to {{ }}: invite/resend outcomes, sys_invitation and sys_member descriptions, both permission_set_granted outcomes, four objects.generated bundles, the action form help and actions.mdx. (4) 408466db: @object-ui/fields resolveActionParams reads helpText through actionCopySource. CI Type Check UI named it, and the seat accepted it as the fifth reader under amendment 6547728288. The console keeps reading ${…} for sentences with no {{ }} hole. (5) e68dae86: the cli extractor pin and two plugin-approvals pins read a parsed description as the envelope (CI Test Core 5/8 and 6/8 on 408466db), and the outcomeMessages TSDoc plus the action-copy-template module note now say the stored-copy rewrite is by hand, naming the semantic entry (triage should-fix).", "tests": "New pins, each reverse-checked: core ActionRunner.copyEnvelope-27948 4 (reverse 2F/2P controls); react useActionTextLocalizer.outcomeEnvelope-27948 3 (2F/1P); runtime action-param-help-envelope-27948 2 (2F); cli i18n-extract-action-copy-envelope-27948 2 (1F/1P); app-shell ActionPreview.copyEnvelope-27948 2 (1F/1P); app-shell resolveActionParams.helpTextEnvelope-27948 3 (2F/1P type pin); lint validate-action-copy-templates 6 new (5 refusal pins failed on reverse); spec action-copy-template 7 new. Suites: spec 773 files / 22871 tests; lint 183/183 files, 7699 tests; platform-objects 104/104, 1474; cli test/i18n 44/44, 419; runtime src/action* 29/29, 521; ui core actions + react hooks/context 108/108, 1263; app-shell metadata-admin previews 157/157, 1713; app-shell src/utils resolveActionParams* 6 files, 77. Type-check: spec, runtime, cli, @object-ui/core and @object-ui/react clean. Fields tsc shows no error in resolveActionParams.ts; the rest of its local errors are unbuilt-dist TS2307 in other files. check-i18n-bundles is in sync. At e68dae86: plugin-approvals 106/106 files, 1366 tests (3 pins fixed: approve-decision-question x2, sys-approval-request.object x1); platform-objects 104/104, 1474; plugin-auth 206/206, 3933; spec src/ui+shared+studio+migrations+source-reading pins 218/218, 7316; cli i18n 44/44 + i18n-extract-action-description 7/7. A whole-cli local run did not finish, and CI ran cli in its shards.", "gates": "dispatch-gates --commands at a8f9f1c7: 116 families, stdin from /dev/null, exit codes recorded before any pipe, all exit 0. --ran: 116 derived, 116 run, 0 NOT-MEASURED. Record: /mnt/project-files/spec-seat-2/r2026-10-30/out/27948-step2-gates-ran-a8f9f1c7.list. 408466db (fields reader) and e68dae86 (test pins + TSDoc) are covered by CI. merge-tree against origin/main is clean. CI on e68dae86: every check-run green (65: Test Core 8/8 + rollup, Test UI 16/16 + rollup, Type Check UI, TypeScript Type Check, Lint & Repo Gates, Check Changeset, Spec property liveness, docs, Build Core/Docs/UI, Live E2E UI); opt-in and dogfood-shard jobs skipped by filter.", "dogfood_battery": "Full dogfood (packages/qa/dogfood pnpm test, under os-verify-lock) at a8f9f1c7: exit 0, Test Files 362 passed / 3 skipped (365), Tests 2922 passed / 28 skipped. 408466db and e68dae86 touch nothing dogfood loads (a ui fields file, tests, TSDoc, a changeset line).", "line_budget": "+1360 / -211 across 57 files vs merge base 776c1d87", "files_changed": [ ".changeset/27948-action-copy-envelope-readers.md", ".changeset/27948-action-copy-template-slots.md", "content/docs/references/api/metadata.mdx", "content/docs/references/api/package-api-assembled.mdx", "content/docs/references/data/object.mdx", "content/docs/references/kernel/metadata-plugin.mdx", "content/docs/references/system/migration.mdx", "content/docs/references/ui/action.mdx", "content/docs/ui/actions.mdx", "packages/cli/src/utils/i18n-extract.ts", "packages/cli/test/i18n-extract-action-copy-envelope-27948.test.ts", "packages/cli/test/i18n-extract-action-description.test.ts", "packages/lint/src/validate-action-copy-templates.test.ts", "packages/lint/src/validate-action-copy-templates.ts", "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/en.objects.generated.ts", "packages/platform-objects/src/apps/translations/es-ES.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/es-ES.objects.generated.ts", "packages/platform-objects/src/apps/translations/ja-JP.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/ja-JP.objects.generated.ts", "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts", "packages/platform-objects/src/apps/translations/zh-CN.objects.generated.ts", "packages/platform-objects/src/identity/action-confirm-one-dialog.test.ts", "packages/platform-objects/src/identity/invitation-delivery-copy.test.ts", "packages/platform-objects/src/identity/invitation-screens-copy.test.ts", "packages/platform-objects/src/identity/invite-outcome-messages.ts", "packages/platform-objects/src/identity/invite-user-action-copy.test.ts", "packages/platform-objects/src/identity/oauth-register-form-types.test.ts", "packages/platform-objects/src/identity/rotate-client-secret-copy.test.ts", "packages/platform-objects/src/identity/sys-api-key-create-expiry.test.ts", "packages/platform-objects/src/identity/sys-invitation.object.ts", "packages/platform-objects/src/identity/sys-member.object.ts", "packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts", "packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts", "packages/plugins/plugin-approvals/src/translations/approve-decision-question.test.ts", "packages/qa/dogfood/test/expression-conformance.ledger.ts", "packages/runtime/src/action-execution.ts", "packages/runtime/src/action-param-help-envelope-27948.test.ts", "packages/spec/liveness/action.json", "packages/spec/src/migrations/entries/semantic/18.action-dialog-outcome-copy-template-holes.ts", "packages/spec/src/system/i18n-resolver.ts", "packages/spec/src/ui/action-copy-template.test.ts", "packages/spec/src/ui/action-copy-template.ts", "packages/spec/src/ui/action-description.test.ts", "packages/spec/src/ui/action-outcome-messages.test.ts", "packages/spec/src/ui/action.form.ts", "packages/spec/src/ui/action.zod.ts", "packages/spec/src/ui/component.zod.ts", "ui/packages/app-shell/src/utils/resolveActionParams.helpTextEnvelope-27948.test.ts", "ui/packages/app-shell/src/views/metadata-admin/previews/ActionPreview.tsx", "ui/packages/app-shell/src/views/metadata-admin/previews/__tests__/ActionPreview.copyEnvelope-27948.test.tsx", "ui/packages/core/src/actions/ActionRunner.ts", "ui/packages/core/src/actions/__tests__/ActionRunner.copyEnvelope-27948.test.ts", "ui/packages/core/src/actions/actionCopyTemplate.ts", "ui/packages/fields/src/resolveActionParams.ts", "ui/packages/react/src/hooks/__tests__/useActionTextLocalizer.outcomeEnvelope-27948.test.tsx", "ui/packages/react/src/hooks/useActionTextLocalizer.ts" ], "deviations": [ "Surface additions beyond the original claim, both seat-ruled: app-shell ActionPreview.tsx + pin (amendment 6546830184, in commit 1), and @object-ui/fields resolveActionParams.ts + pin (amendment 6547728288, in a separate 4th commit because the branch was already pushed; no force push).", "Left as is by seat ruling: ui/packages/components/src/renderers/action/action-icon.tsx lines 319/332 tooltip `schema.label || schema.description`. It is unreachable from a parsed slot, since the localizer always fills label and a row's description is not a slot, and UIActionSchema still types description as a string.", "Test pins outside the reader sweep (non-test sources) went red in CI on 408466db: cli i18n-extract-action-description.test.ts and plugin-approvals sys-approval-request.object.test.ts + approve-decision-question.test.ts. Fixed in e68dae86 at the seat's order; local plugin-approvals measured 3 failures where the lane named 1.", "The PR body Acceptance notes were patched by the seat (issue_patch with its signature) from out/27948-step2-pr-body-v2.md; a card thread cannot sign the body.", "The harness's post-create assignee and reviewer steps were not done, per FRAME (the seat lands; no assign, no review request, no MCP writes)." ], "mcp_calls": "Reads only: mcp__github__pull_request_read (get, get_check_runs), mcp__github__get_job_logs and get_check_run (Type Check UI, the Test UI rollup on a8f9f1c7 that was cancelled by the next push, the Test Core 6/8 and planner logs on 408466db), mcp__github__issue_read on #27948. 0 writes.", "api_writes": "2: relay pr_create (fleet-write run 41230166641, PR #28463); this os-dev-report comment via post-stamped. Plus git pushes of claude/project-thread-2br7jq (a8f9f1c7, 408466db, e68dae86). The PR body patch was the seat's issue_patch.", "open_questions": [], "out_of_scope_findings": [ "carrier: #28464 (skills card, filed by the seat) · skills/objectstack-ui/rules/actions.md line 23 lists {{ }} copy holes for confirmText/successMessage/errorMessage only · dedupe words: skills action copy template holes description helpText outcomeMessages", "carrier: objectstack-ai/hotcrm#2384 (filed by the seat, Blocked-by #27948) · knowledge_article.actions.ts lines 121-123/144-146 outcomeMessages ${result.helpful}/${result.notHelpful} plus the four objects.service.ts translations convert to {{ result.* }} at the pin bump · dedupe words: hotcrm outcomeMessages dollar-brace", "carrier: none (comment only) · packages/plugins/plugin-auth/src/role-change-granted-set.ts line 29 comment still spells ${result.grantedPermissionSet.label}; outside the surface · dedupe words: role-change granted set comment dollar-brace" ] }