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
spec: type the remaining person-facing text slots as template slots — confirmText ×3, successMessage / errorMessage ×6, emptyText ×2 (ADR-0032 Decision 2 and 3; the same mechanism as #24798) #24814
Filing gate ③ — the maintainer's 「同意」 on the director's proposal 「另立一张 spec 卡把剩下九处文本槽统一改成 template 槽」 (director's chat, 2026-10-20), after 「是否建议统一到 cel」 was answered no: ADR-0032 keeps two shapes — whole-field CEL for logic and values, {{ }} templates for person-facing text — and what is not yet unified is the slot TYPE, not the dialect. Filed by the director seat (summon #45 continuation 1, session_01Lg6GQ4xjqmvEzYv78d6H7i). ⛔ Not a claim.
Reader who acts: the domain:spec seat, serial after #24798's spec PR (same mechanism: TemplateExpressionInputSchema, the bound-root judge at the three doors, the formatter whitelist). Clause-②: yes (widening), minor, contract review at CONTRACT_REVIEW_TIER; not before 18.0 GA. The renderer halves are objectui cards filed per slot family when the spec release is installable there (ruling D on #24591); the bulk-action one is objectstack-ai/objectui#13183.
Template-typed today: the flow text slots (notify title / message, screen title, http url and headers — automation/flow-text-slot-template.ts), the AI prompt templates (ai/model-registry.zod.ts:121–122) and the deprecated titleFormat (data/object.zod.ts:2843). Still z.string() although person-facing text that carries holes by purpose:
slot
where
confirmText
ui/component.zod.ts:4036, :4136, :4286 (component action buttons; the bulk-action slot at ui/bulk-action.zod.ts:403 is #24798)
Deliberately NOT in scope, literal by design: title (ui/component.zod.ts:2252 「Rendered as-is in EVERY locale」, :2607, :5845, :5848; ui/chart.zod.ts:512), placeholder and helpText (ui/action.zod.ts:356, :358; ui/bulk-action.zod.ts:285), a validation rule's message (ui/component.zod.ts:6510), diagnostic and error message fields in automation execution and bpmn interop. To confirm or exclude at claim: flow successMessage / errorMessage on AutomationResult (automation/flow.zod.ts:1085–1086), a result envelope rather than authored display text.
Every slot in scope is translated per locale where the i18n resolver reaches it (system/i18n-resolver.ts:616, confirmText and successMessage), which is why a template with named holes is the shape and a CEL expression is not: translators move holes, nobody translates an expression.
What done looks like
Each listed slot is a TemplateExpressionInputSchema slot whose scope's bound roots are declared (the action's record, count, params — as each family has them); an unbound root or a single-brace token is refused by name at objectstack validate, compile and the publish gate through the flow text slots' judge; the example apps' authored strings still parse; artifacts regenerated. ⛔ No new engine, no new delimiter, no CEL inside holes. Producers measured at claim (git grep for each key carrying {{, examples and hotcrm).
Dedupe
#24798 (the first member, ruled B); objectstack-ai/objectui#13154 (closed, moved). Open domain:spec cards scanned by title and body for template slot / successMessage / emptyText / confirmText: see the director's ledger block for the count read at filing; no card on typing these slots.
Claim: PM loop round 2
Session: session_01UNcc5YAhWPpKE7Ttou4fqu (the card thread; seat session session_013odhA26dvzy7pQmevdTTvy posts this claim)
Account: os-tesla
Branch: claude/project-thread-j9tmar
Worktree: objectstack-issue-24814
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017)
File surface: packages/spec/src/ui/component.zod.ts + tests, packages/lint/src/validate-expressions.ts (the three-door judge wiring, outside the bulk block) + tests, packages/spec/src/system/i18n-resolver.ts if the override comparison needs it, generated references, the changeset (stop on breach; explain in the report). ⛔ no objectui edit.
Container & model: L, mode:thread, model: default tier (dispatch-gates --tier: "no path-derived mandate", floor sonnet · default opus · ceiling fable), effort: high
Clause-②: yes (widening, per the card; the dev measures for a refusal half and stops to report a split if one exists)
Metadata-protocol: added
Responsibility: n/a — not a defect card
Thread-read: none
Serial constraints cleared: #24798 closed by PR #24947 (merged as 925fd077, 2026-10-23T12:53Z), this card's Blocked-by:; 18.0 GA shipped (@objectstack/spec@18.0.0). pm:blocked removed by this claim. Parallel: #25205 edits the bulk block of validate-expressions.ts.
The platform lets the card thread push only its designated thread branch, so Branch: records that branch, not claude/issue-24814-<slug>. Dispatch order: the project's shared folder, spec-seat-1/dispatch-24814.md. The seat runs a CONTRACT_REVIEW_TIER review before the queue.
{
"issue": 24814,
"status": "needs_decision",
"branch": "claude/project-thread-j9tmar",
"pr": null,
"session": "session_01UNcc5YAhWPpKE7Ttou4fqu",
"premise_still_valid": false,
"summary": "Stopped at the order's measurement point; nothing written (branch pushed empty at 925fd077). Measured at objectstack 925fd077, objectui f2f4b7d (pin 5fef032b), hotcrm 1e71d5e. (1) Clause-2 split exists: a straight swap to TemplateExpressionInputSchema refuses a blank string that parses today on all 11 slots (probe: action:button parses '' for all three keys; the template arm answers 'needs a non-blank template'), and door findings as errors would refuse text that parses today (probe: 'Run ${record.name}?' parses on action:button). In the #24947 form (blank arm + warning-only doors) the widening ships alone, and the refusal is its own BREAKING minor, as #25205 is for the bulk slot. (2) But the slots' delivered dialect is not the double-brace one: the shared ActionRunner fills dollar-brace holes in confirmText through ExpressionEvaluator with the row bound as record (objectui packages/core/src/actions/ActionRunner.ts:1468-1471, :2752), fills dollar-brace result.* in successMessage through substituteScopeTokens (:1718-1721), and interpolates nothing in errorMessage (:1767); it fills no double-brace hole in any of the three. The spec's own ActionSchema.successMessage describe declares the dollar-brace result.* scope (ui/action.zod.ts:1351), with 4 producers in platform-objects (sys-invitation/sys-member/sys-organization/sys-user, 'Invitation sent to ${result.email}'). The closed-scope judge reads '${record.name}' as a single-brace token and prescribes 'Delete $' + double-brace record.name, a spelling that renders worse (probe output). (3) The parsed shape moves string to envelope, and three renderer reads break on it (read at objectui f2f4b7d, not run): composeSuccessMessage skips a non-string, so the authored success toast silently becomes the default (:1718); errorMessage falls to the generic failure text (:1767-1772); both emptyText renderers place the value as a React child (plugin-detail HistoryTimeline.tsx:213, components data-list.tsx:274), which is a React 31 crash when an object reaches it. So like #24947 the renderer halves must land first. (4) emptyText x2 (component.zod.ts:2477 record:history, :4717 element:repeater; :3359 is record_picker, already I18nLabelSchema) has no hole by purpose: neither renderer binds a scope, and :2477 is literal by design. (5) Producers: zero page-row producers of any of the 11 slots, holes or not, across examples, platform-objects, skills (925fd077) and hotcrm (1e71d5e). (6) i18n: the row keys confirmText/successMessage/errorMessage are not translated at all (PAGE_COMPONENT_COPY_KEYS, i18n-resolver.ts:2188, is title/description/label/placeholder/emptyText); :616 is the registered-action lookup. The page emptyText overlay has no packaged-base comparison, so the #24947 comparison fix does not apply. (7) AutomationResult successMessage/errorMessage (flow.zod.ts ~:1085): excluded, a result envelope served verbatim.",
"tests": "No code change, so no suite run. Probes (tsx over packages/spec src at 925fd077, probe file deleted): closedScopeTemplateRefusal over 7 texts with roots [record]; TemplateExpressionInputSchema.safeParse over the same 7; ActionButtonPropsSchema / RecordHistoryProps / ElementRepeaterPropsSchema over '' and a dollar-brace sentence (all parse today).",
"gates": "NOT MEASURED: no diff, so nothing derived to run.",
"dogfood_battery": "n/a: no PR",
"mcp_calls": "0",
"api_writes": "1: POST /repos/objectstack-ai/objectstack/issues/24814/comments (this os-dev-report, via post-stamped)",
"open_questions": [
{
"question": "emptyText x2 (record:history :2477, element:repeater :4717): type them as template slots?",
"options": [
"A exclude: no renderer binds a scope for an empty state, :2477 is literal by design, zero producers; the card's count drops to 9",
"B type them over zero roots: a template type whose every hole is refused, plus a served-shape change that crashes two renderers until objectui reads the envelope"
],
"recommendation": "A. Real need: zero producers and no scope to fill. AI-error: a declared template slot over no roots advertises holes nothing delivers. Startup focus: no pull, no widening."
},
{
"question": "The 9 action-row slots: the runner key they forward to is shared with ActionSchema's registered-action confirmText/successMessage, whose delivered dialect is dollar-brace (spec describe + 4 platform-objects producers). How to unify?",
"options": [
"A renderer-first, one family: objectui moves the runner's confirmText/successMessage/errorMessage to read the envelope and fill double-brace holes through the template engine (roots: confirmText record, successMessage result and record, errorMessage none until a scope is ruled), pin bump; then this card types the 9 row slots in the #24947 form (blank arm, warning-only doors, the judge taught to prescribe double-brace for a dollar-brace token); ActionSchema's two slots and the 4 platform-objects sentences move in the same family (a conversion), or a sibling card",
"B spec-only now in the #24947 form: lands a served-shape change that silently drops the authored success/error toasts in the shipping console (measured) and declares holes the runner does not fill",
"C park the card: zero producers of any row slot; keep z.string until a producer pulls"
],
"recommendation": "A. Long-term: one runner key, one dialect (ADR-0032 Decision 3), and the renderer has to read the envelope first anyway (the #24947 landing order). AI-error: the doors' prescription must be a spelling the runner fills, so the runner moves before the doors speak. Real need and startup focus point at C (zero row producers). Shipping B breaks shipped copy. If the maintainer weighs zero pull over the standing approval, C."
},
{
"question": "Clause-2 split for whichever slots stay in scope",
"options": [
"A this card = widening only (envelope arm, declared roots, blank arm kept, door findings as warnings), minor, Metadata-protocol semantic (the slot's meaning moves from the runner's dollar-brace reading to template holes); refusal of blank and of door findings = its own BREAKING minor card, as #25205",
"B one BREAKING minor carrying both halves"
],
"recommendation": "A, the amended rule's form and the #24947 / #25205 precedent."
}
],
"out_of_scope_findings": [
"not filed (reach not measured at a public door: judge probe only, packages/spec src at 925fd077) · closedScopeTemplateRefusal reads a dollar-brace token as a single-brace token and prescribes the hole with the dollar sign kept ('Delete ${record.name}?' is told to become 'Delete $' + double-brace record.name). Today it runs only on bulk confirmText, where no producer writes a dollar-brace token; it bites when a slot whose authors write dollar-brace joins the judge, so it belongs to whichever card puts the action-copy slots under it (question 2) · dedupe: closedScopeTemplateRefusal dollar brace, single-brace prescription, template judge dollar",
"carrier: the action-copy family card (question 2) · the runner's errorMessage interpolates nothing, so a row errorMessage has no scope to declare until one is ruled · noted, not filed"
]
}
The console this repo ships does not carry it yet..objectui-sha on main is 6fb31dcc9954. The compare 8e90779dcb...6fb31dcc9954 reads behind: the pin predates the fix.
State: stays pm:blocked. The next .objectui-sha move past 8e90779dcb (one is due after the 18.1.0 cut) unlocks it. The unlock scan reads this restart condition.
Claim: PM loop round 1
Session: session_01PC4NPXPgPmpDRG9DdDnyXT
Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-24814-action-copy-template-slots
Worktree: objectstack-issue-24814
Domain: domain:spec
Seat: domain:spec#2 (seat post #18549)
File surface: packages/spec/src/ui/component.zod.ts (the 9 component action-row slots confirmText / successMessage / errorMessage at about :4036–:4038, :4136–:4138, :4286–:4288 on origin/maine649c2d91e), packages/spec/src/ui/action.zod.ts (ActionSchema.confirmText / successMessage), the template-slot judge and its prescription (the #24947 mechanism: TemplateExpressionInputSchema, the bound-root judge at validate / compile / the publish gate) in packages/spec and packages/lint, with door findings as warnings; a domain:server rider the ruling names: the 4 ${result.*} success sentences in packages/platform-objects (sys-invitation, sys-member, sys-organization, sys-user), converted; tests; regenerated ui shards and references; one changeset .changeset/24814-action-copy-template-slots.md. emptyText is out of scope by the ruling. (stop on breach; explain in the report)
Container & model: M, mode:cloud (surface is packages/spec/src/** plus packages/lint and packages/platform-objects ⇒ build-heavy, one card per container), model: default (TIER_DEFAULT; dispatch-gates --tier for this surface: no path-derived mandate), effort: high. Clause-②: yes ⇒ a CONTRACT_REVIEW_TIER contract review is owed before the queue.
Clause-②: yes (widening)
Metadata-protocol: semantic
Responsibility: n/a — not a defect card
Ruling-ref: 6365736632 (letter A, maintainer 「A 先改控制台,再统一一族」, read now on this card); unlock 6377096620 (.objectui-sha2958f17e9c6a carries objectui PR #13409's envelope reader)
Thread-read: 6377096620
Serial constraints cleared: PR #25612 (#25511, this seat) edits the same file, ui/component.zod.ts (the record:detailshideEmpty describe at about :1440, a disjoint region), and regenerates the same ui reference family. It is ACCEPTed (6382491996) and lands first. This card's dev merges origin/main once before opening its PR, and the generated ui family lands one PR at a time through scripts/pm/os-regen-merge.sh. Fold-or-serial: serial, not foldable (a different defect form, gate ①). No other open PR touches ui/component.zod.ts, ui/action.zod.ts, the template judge or those platform-objects files (all 13 open PRs' file lists re-read 2026-10-24T09:25Z); none of the open pm:dispatched cards in other lanes declares these files or packages/platform-objects/src/identity/sys-{invitation,member,organization,user}*.
Premise: holds on origin/maine649c2d91e — the 9 slots are still z.string() at the lines above; the previous attempt's branch claude/project-thread-j9tmar (925fd0772c, empty) was released at 6365681003 and is not reused.
Claim addendum (amends the file surface of 6382532929) · domain:spec seat 2 · 2026-10-24T09:26Z
File surface, added: packages/spec/src/system/i18n-resolver.ts (the translated confirmText / successMessage reads, about :616) and packages/cli/src/utils/i18n-extract.ts (domain:server), only as far as the slots' new type needs them, as the #24947 precedent did. Both files are also on this seat's in-flight #25459 (claim 6381897923, the form-section functions there). They are disjoint regions and ordinary concurrency, not a single-writer path: whichever PR lands second merges origin/main.
Back end:mode:cloud, cloud session session_01DcqhBfbrsH8ACDsMQ62Hzq (created by this seat's session session_01PC4NPXPgPmpDRG9DdDnyXT through create_session, outcome branch as claim 6382532929 names). Liveness probes and any takeover read that session first; the claim stays the identity.
Order given: the ruling's four bullets verbatim, the earlier report 6365644870 as leads to re-measure, the unlock's downstream shape check (compile the pinned console against the branch), the consumer-package test list, the Writes: line.
{
"issue": 24814,
"status": "done",
"branch": "claude/issue-24814-action-copy-template-slots",
"pr": "https://github.com/objectstack-ai/objectstack/pull/25637",
"session": "session_01DcqhBfbrsH8ACDsMQ62Hzq",
"premise_still_valid": true,
"summary": "Typed the action-copy family as ADR-0032 template slots in the #24947 form (ruling 6365736632, letter A). That covers the 9 page action row slots (action:button / action:icon / the action:group-menu member x confirmText, successMessage, errorMessage) and ActionSchema.confirmText / successMessage, which InlineActionSchema picks and which keep the inline locale map arm. The blank arm is kept. The roots were measured at the pinned objectui ActionRunner (2958f17e9c6a): confirmText over record, plus params only beside static values; successMessage over result and record; errorMessage over none, because the runner shows it as written (re-measured: it interpolates nothing). They are exported from @objectstack/spec/ui. The closed-scope judge now reads ${...} with its $, so it prescribes {{ x }}, never ${{ x }}, and it names every hole in a zero-root slot as literal text. A new lint pass (validate-action-copy-templates.ts, inside validateStackExpressions) judges registered actions, page rows and inline properties.action at validate / compile / the publish gate. 'page' joins runtimeTypes, and every finding is a warning (an advisory, never a 422). The 4 named platform-objects ${result.email} sentences and 9 more plugin-approvals ${result.request.record_title} toasts were converted in every locale (see deviations). The i18n resolver and extractor read the envelope, and three expression-surface ledger rows classify the 11 new positions. The renderer half was re-verified at the pin, and the pinned console compiles against this spec.",
"tests": "All at head e875a564 unless noted. packages/spec: new src/ui/action-copy-template.test.ts, plus 5 new judge cases in flow-text-slot-template.test.ts. Full suite at 8fed93a: 789 of 793 files passed, 3 failed, 1 skipped (Tests 3 failed, 22956 passed); all 3 fixed (action.test.ts, component-report-items-action-members-typed.pin.test.ts, export-origins artifact). After the restructure, src/ui + src/system + the judge: Test Files 211 passed (211), Tests 7614 passed. bulk-confirm-template, action-confirm-params-guard, component-props-unknown-members.pin and i18n-resolver* are inside that set and green. packages/lint: new validate-action-copy-templates.test.ts (13, real runRuntimeAuthoringRules on page and action writes: 1 advisory, 0 errors). Full suite: Test Files 159 passed (159), Tests 7250 passed (validate-expressions*, authoring-rule-wiring, runtime-gate* included). packages/platform-objects: 94/94 files, 1343 tests. plugin-approvals: 95/95, 1244. cli --project unit: 327/327, 4812 (i18n-bulk-action-coverage and the i18n tests included). metadata-protocol protocol.runtime-authoring-gate (197), rest meta-view-container-i18n (11), runtime action-execution-destructive (63), service-i18n authored-translations (16): all pass. dogfood expression-conformance.test.ts: 8/8, with 3 new ledger rows. Before they existed the census reported 11 UNCLASSIFIED positions, the proof the census sees them. NOT MEASURED: member-transfer-ownership-step-down.dogfood.test.ts (edited to read the served copy through templateSlotSource; it boots the showcase, so CI's battery runs it). Downstream shape check: OBJECTUI_ROOT=../objectui bash scripts/build-console.sh with objectui at 2958f17e9c6a against this branch's built spec (pre-merge head 3c1eb1e0). Turbo 36/36 + 34/34 successful, the console's 'tsc && vite build' completed, and the canary read '✓ Console bundle carries THIS tree's @objectstack/spec, and only it'. Ablation: isDollarToken forced to 'return false' via scripts/ablation-replace.mjs (anchor 1->0, blob e12c4385->38c36986) gave 4 failed / 41 passed in flow-text-slot-template.test.ts; restore put the blob back at e12c4385 with git diff HEAD empty. Producers measured at e649c2d91e: 13 authored action-copy sentences with ${ (4 platform-objects, 9 plugin-approvals) and 0 with {{. On HEAD: 0 with ${, 13 with {{. examples/**: 0 either way.",
"gates": "Local, at e875a564. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 119 families (46 paths vs merge base 481ac6211). Each was run with its exit captured before any pipe. 115 exited 0 on the first pass. 4 exited 3, PREREQUISITE NOT MET: check-plugin-teardown-shape --self-test (a shallow clone; fetched the pinned fixture 621a4876), spec check:skill-examples, check:dual-build-cjs-loads and check:i18n-coverage (unbuilt packages). After turbo run build (77/77) all 4 exited 0. --ran with 'cmd :: exit N' answered: '119 derived famil(ies) accounted for — 119 run, 0 NOT-MEASURED (a DERIVED zero — all 119 recorded an exit code and none of them is 3)'. Extra: spec check:generated reported 'All 14 generated artifacts are up to date' (after --fix regenerated api-surface/ui.json, export-origins/ui.json and the ui/data/kernel reference pages, and after the merge regenerated component.mdx via spec build + gen:docs on the committed merge). typecheck exited 0 on spec, lint, cli, platform-objects, plugin-approvals and dogfood. check:dev-prereqs exited 0 and check:skill-frame-freshness exited 0. CI check-runs on #25637: in_progress at report time, not awaited per contract.",
"dogfood_battery": "n/a — no `!` in the PR title and the Clause-② arm is (widening)",
"mcp_calls": "0",
"api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay, pr_create #25637 with assignee sunhaolin, read back byte-identical); POST /repos/objectstack-ai/objectstack/issues/24814/comments (this os-dev-report, via post-stamped)",
"deviations": [
"plugin-approvals conversion beyond the ruling's '4': packages/plugins/plugin-approvals/src/sys-approval-request.object.ts carried 9 ${result.request.record_title} success toasts, which the earlier report's census missed. They were converted with their 3 translated bundles, the regenerated en bundle and 3 test pins. The four bounded in-place conditions were checked: same defect class; a mechanical conversion of a pinned form; no open PR touches the file (all open PRs' file lists read, only #25612 overlaps, on component.zod.ts); same gate family. Owed: the seat adds packages/plugins/plugin-approvals/src/sys-approval-request.object.ts and src/translations/* (plus the 2 test files) to the claim's file surface.",
"Files outside the claimed surface, each needed by the slots' new type: packages/qa/dogfood/test/expression-conformance.ledger.ts (3 rows for the 11 new template positions) and member-transfer-ownership-step-down.dogfood.test.ts (reads the served copy through templateSlotSource); packages/cli/src/commands/explain.ts (confirmText row type); packages/plugins/plugin-approvals test pins; platform-objects translated-locale successMessage values (hand-edited, which AGENTS.md allows; no source-hash entries exist for them).",
"The slot's template input is built at each declaring .zod.ts file (component.zod.ts actionRowCopy, action.zod.ts actionCopy) rather than inside ui/action-copy-template.ts. The expression-surface census scans only *.zod.ts, and a first cut in the plain module was invisible to it.",
"Harness attribution reminder not followed in the PR body: the body carries only the AGENTS.md session-URL footer, per os-dev.md's precedence. Commit trailers use the model-free pair.",
"Not subscribed to PR activity, per os-dev.md (the dispatch contract outranks the platform's auto-subscribe posture).",
"No reverse type probe file was planted. The downstream console compile against the rebuilt dist stands as the cross-package type read."
],
"files_changed": [
".changeset/24814-action-copy-template-slots.md",
"content/docs/references/data/object.mdx",
"content/docs/references/kernel/metadata-plugin.mdx",
"content/docs/references/ui/action.mdx",
"content/docs/references/ui/component.mdx",
"packages/cli/src/commands/explain.ts",
"packages/cli/src/utils/i18n-extract.ts",
"packages/lint/src/authoring-rules.ts",
"packages/lint/src/runtime-gate.page-visibility-predicate-writes.test.ts",
"packages/lint/src/validate-action-copy-templates.test.ts",
"packages/lint/src/validate-action-copy-templates.ts",
"packages/lint/src/validate-expressions.test.ts",
"packages/lint/src/validate-expressions.ts",
"packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts",
"packages/platform-objects/src/identity/invite-user-action-copy.test.ts",
"packages/platform-objects/src/identity/sys-{invitation,member,organization,user}.object.ts",
"packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts",
"packages/plugins/plugin-approvals/src/approval-service.test.ts",
"packages/plugins/plugin-approvals/src/subject-title-reader-fls.integration.test.ts",
"packages/plugins/plugin-approvals/src/sys-approval-request.object.ts",
"packages/plugins/plugin-approvals/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts",
"packages/plugins/plugin-approvals/src/translations/success-copy-names-request-i18n.test.ts",
"packages/qa/dogfood/test/expression-conformance.ledger.ts",
"packages/qa/dogfood/test/member-transfer-ownership-step-down.dogfood.test.ts",
"packages/spec/api-surface/ui.json",
"packages/spec/export-origins/ui.json",
"packages/spec/src/automation/flow-text-slot-template.test.ts",
"packages/spec/src/automation/flow-text-slot-template.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.test.ts",
"packages/spec/src/ui/action.zod.ts",
"packages/spec/src/ui/component-props-unknown-members.pin.test.ts",
"packages/spec/src/ui/component-report-items-action-members-typed.pin.test.ts",
"packages/spec/src/ui/component.zod.ts",
"packages/spec/src/ui/index.ts"
],
"open_questions": [
{
"question": "ActionSchema.errorMessage and outcomeMessages: the ruling names only ActionSchema's confirmText / successMessage, so errorMessage stays an I18nLabel and outcomeMessages keeps the ${result.*} reading the runner applies to it. Bring them into the family?",
"options": [
"A leave as is (this PR; follows the ruling's letter)",
"B a follow-up card types ActionSchema.errorMessage over no roots and moves outcomeMessages to {{ result.* }} with a runner change first"
],
"recommendation": "A for this PR, decided as an implementation trade-off within the ruling. B only if the maintainer wants one dialect across every runner copy key; outcomeMessages needs the renderer first, since the runner fills only ${…} there."
},
{
"question": "The ${…}-aware prescription is applied only in the closed-scope judge (closedScopeTemplateRefusal). The flow text slots' judge (textSlotTemplateRefusal) is unchanged. Extend it there too?",
"options": [
"A closed scope only (this PR)",
"B the flow judge as well"
],
"recommendation": "A: the flow text slots never carried a ${…} dialect, and their refusal text is pinned. Changing it would widen scope with no producer pulling."
},
{
"question": "The refusal half (blank and door findings as errors) for this family, the analogue of #25205, is to be filed when this card lands per the ruling.",
"options": [
"A the seat files it at landing"
],
"recommendation": "A, as the ruling orders. It flips actionCopyTemplateIssues' severity and drops the ADVISORY suffix."
}
],
"out_of_scope_findings": [
"carrier: none · Studio's action inspector and preview (objectui ActionDefaultInspector / ActionPreview) read the three keys as I18nLabel maps and would show an envelope as 'template', as noted on objectui #13409. Authoring surface; noted in Acceptance notes, not filed · dedupe words: ActionDefaultInspector template envelope, ActionPreview confirmText envelope",
"carrier: objectui #13183 · the expression-surface ledger row template-bulk-confirm-text still says no evaluator is found for the bulk confirmText; restate it when that renderer lands · noted, not filed"
]
}
Claim addendum (amends the file surface of 6382532929 / 6382542433, this round) · domain:spec seat 2 · 2026-10-24T12:22Z
File surface, added, to cover the in-place extension PR #25637 declares (its body's Deviations section, the 13-producer census):
packages/plugins/plugin-approvals/src/sys-approval-request.object.ts, src/translations/*.objects.generated.ts, and the test pins approval-service.test.ts, subject-title-reader-fls.integration.test.ts, translations/success-copy-names-request-i18n.test.ts (domain:server): the 9 ${result.request.record_title} success toasts the earlier census missed, converted to the template spelling.
packages/qa/dogfood/test/expression-conformance.ledger.ts (3 rows for the 11 new template positions) and member-transfer-ownership-step-down.dogfood.test.ts (reads served copy through templateSlotSource).
packages/cli/src/commands/explain.ts (the confirmText row type).
The four in-place conditions, checked by this seat: the same defect class (action copy written in the ${…} dialect); a mechanical conversion of a pinned form; no other claim on those files (PR #25600 on plugin-approvals merged); the same gate family. Concurrency note: PR #25626 (plugin-auth, open, ready) also edits platform-objects/src/identity/sys-user.object.ts and the four platform-objects locale bundles. Different regions; whichever lands second merges origin/main and regenerates bundles with check-i18n-bundles --write, never by hand.
ACCEPT → PR #25637 comment 6384169307 · domain:spec seat 2 · 2026-10-24T12:24Z · head e875a56445. A CONTRACT_REVIEW_TIER contract review is owed before landing (needs:contract-review on the PR). At the merge, the seat files the refusal-half card the ruling orders.
Activity
objectstack-fleet commentedon Oct 23, 2026
Claim: PM loop round 2
Session:
session_01UNcc5YAhWPpKE7Ttou4fqu(the card thread; seat sessionsession_013odhA26dvzy7pQmevdTTvyposts this claim)Account:
os-teslaBranch:
claude/project-thread-j9tmarWorktree:
objectstack-issue-24814Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface:
packages/spec/src/ui/component.zod.ts+ tests,packages/lint/src/validate-expressions.ts(the three-door judge wiring, outside the bulk block) + tests,packages/spec/src/system/i18n-resolver.tsif the override comparison needs it, generated references, the changeset (stop on breach; explain in the report). ⛔ no objectui edit.Container & model:
L,mode:thread,model: default tier (dispatch-gates --tier: "no path-derived mandate", floor sonnet · default opus · ceiling fable),effort: highClause-②: yes (widening, per the card; the dev measures for a refusal half and stops to report a split if one exists)
Metadata-protocol: added
Responsibility:
n/a — not a defect cardThread-read: none
Serial constraints cleared: #24798 closed by PR #24947 (merged as
925fd077, 2026-10-23T12:53Z), this card'sBlocked-by:; 18.0 GA shipped (@objectstack/spec@18.0.0).pm:blockedremoved by this claim. Parallel: #25205 edits the bulk block ofvalidate-expressions.ts.The platform lets the card thread push only its designated thread branch, so
Branch:records that branch, notclaude/issue-24814-<slug>. Dispatch order: the project's shared folder,spec-seat-1/dispatch-24814.md. The seat runs aCONTRACT_REVIEW_TIERreview before the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
os-dev-report
{ "issue": 24814, "status": "needs_decision", "branch": "claude/project-thread-j9tmar", "pr": null, "session": "session_01UNcc5YAhWPpKE7Ttou4fqu", "premise_still_valid": false, "summary": "Stopped at the order's measurement point; nothing written (branch pushed empty at 925fd077). Measured at objectstack 925fd077, objectui f2f4b7d (pin 5fef032b), hotcrm 1e71d5e. (1) Clause-2 split exists: a straight swap to TemplateExpressionInputSchema refuses a blank string that parses today on all 11 slots (probe: action:button parses '' for all three keys; the template arm answers 'needs a non-blank template'), and door findings as errors would refuse text that parses today (probe: 'Run ${record.name}?' parses on action:button). In the #24947 form (blank arm + warning-only doors) the widening ships alone, and the refusal is its own BREAKING minor, as #25205 is for the bulk slot. (2) But the slots' delivered dialect is not the double-brace one: the shared ActionRunner fills dollar-brace holes in confirmText through ExpressionEvaluator with the row bound as record (objectui packages/core/src/actions/ActionRunner.ts:1468-1471, :2752), fills dollar-brace result.* in successMessage through substituteScopeTokens (:1718-1721), and interpolates nothing in errorMessage (:1767); it fills no double-brace hole in any of the three. The spec's own ActionSchema.successMessage describe declares the dollar-brace result.* scope (ui/action.zod.ts:1351), with 4 producers in platform-objects (sys-invitation/sys-member/sys-organization/sys-user, 'Invitation sent to ${result.email}'). The closed-scope judge reads '${record.name}' as a single-brace token and prescribes 'Delete $' + double-brace record.name, a spelling that renders worse (probe output). (3) The parsed shape moves string to envelope, and three renderer reads break on it (read at objectui f2f4b7d, not run): composeSuccessMessage skips a non-string, so the authored success toast silently becomes the default (:1718); errorMessage falls to the generic failure text (:1767-1772); both emptyText renderers place the value as a React child (plugin-detail HistoryTimeline.tsx:213, components data-list.tsx:274), which is a React 31 crash when an object reaches it. So like #24947 the renderer halves must land first. (4) emptyText x2 (component.zod.ts:2477 record:history, :4717 element:repeater; :3359 is record_picker, already I18nLabelSchema) has no hole by purpose: neither renderer binds a scope, and :2477 is literal by design. (5) Producers: zero page-row producers of any of the 11 slots, holes or not, across examples, platform-objects, skills (925fd077) and hotcrm (1e71d5e). (6) i18n: the row keys confirmText/successMessage/errorMessage are not translated at all (PAGE_COMPONENT_COPY_KEYS, i18n-resolver.ts:2188, is title/description/label/placeholder/emptyText); :616 is the registered-action lookup. The page emptyText overlay has no packaged-base comparison, so the #24947 comparison fix does not apply. (7) AutomationResult successMessage/errorMessage (flow.zod.ts ~:1085): excluded, a result envelope served verbatim.", "tests": "No code change, so no suite run. Probes (tsx over packages/spec src at 925fd077, probe file deleted): closedScopeTemplateRefusal over 7 texts with roots [record]; TemplateExpressionInputSchema.safeParse over the same 7; ActionButtonPropsSchema / RecordHistoryProps / ElementRepeaterPropsSchema over '' and a dollar-brace sentence (all parse today).", "gates": "NOT MEASURED: no diff, so nothing derived to run.", "dogfood_battery": "n/a: no PR", "mcp_calls": "0", "api_writes": "1: POST /repos/objectstack-ai/objectstack/issues/24814/comments (this os-dev-report, via post-stamped)", "open_questions": [ { "question": "emptyText x2 (record:history :2477, element:repeater :4717): type them as template slots?", "options": [ "A exclude: no renderer binds a scope for an empty state, :2477 is literal by design, zero producers; the card's count drops to 9", "B type them over zero roots: a template type whose every hole is refused, plus a served-shape change that crashes two renderers until objectui reads the envelope" ], "recommendation": "A. Real need: zero producers and no scope to fill. AI-error: a declared template slot over no roots advertises holes nothing delivers. Startup focus: no pull, no widening." }, { "question": "The 9 action-row slots: the runner key they forward to is shared with ActionSchema's registered-action confirmText/successMessage, whose delivered dialect is dollar-brace (spec describe + 4 platform-objects producers). How to unify?", "options": [ "A renderer-first, one family: objectui moves the runner's confirmText/successMessage/errorMessage to read the envelope and fill double-brace holes through the template engine (roots: confirmText record, successMessage result and record, errorMessage none until a scope is ruled), pin bump; then this card types the 9 row slots in the #24947 form (blank arm, warning-only doors, the judge taught to prescribe double-brace for a dollar-brace token); ActionSchema's two slots and the 4 platform-objects sentences move in the same family (a conversion), or a sibling card", "B spec-only now in the #24947 form: lands a served-shape change that silently drops the authored success/error toasts in the shipping console (measured) and declares holes the runner does not fill", "C park the card: zero producers of any row slot; keep z.string until a producer pulls" ], "recommendation": "A. Long-term: one runner key, one dialect (ADR-0032 Decision 3), and the renderer has to read the envelope first anyway (the #24947 landing order). AI-error: the doors' prescription must be a spelling the runner fills, so the runner moves before the doors speak. Real need and startup focus point at C (zero row producers). Shipping B breaks shipped copy. If the maintainer weighs zero pull over the standing approval, C." }, { "question": "Clause-2 split for whichever slots stay in scope", "options": [ "A this card = widening only (envelope arm, declared roots, blank arm kept, door findings as warnings), minor, Metadata-protocol semantic (the slot's meaning moves from the runner's dollar-brace reading to template holes); refusal of blank and of door findings = its own BREAKING minor card, as #25205", "B one BREAKING minor carrying both halves" ], "recommendation": "A, the amended rule's form and the #24947 / #25205 precedent." } ], "out_of_scope_findings": [ "not filed (reach not measured at a public door: judge probe only, packages/spec src at 925fd077) · closedScopeTemplateRefusal reads a dollar-brace token as a single-brace token and prescribes the hole with the dollar sign kept ('Delete ${record.name}?' is told to become 'Delete $' + double-brace record.name). Today it runs only on bulk confirmText, where no producer writes a dollar-brace token; it bites when a slot whose authors write dollar-brace joins the judge, so it belongs to whichever card puts the action-copy slots under it (question 2) · dedupe: closedScopeTemplateRefusal dollar brace, single-brace prescription, template judge dollar", "carrier: the action-copy family card (question 2) · the runner's errorMessage interpolates nothing, so a row errorMessage has no scope to declare until one is ruled · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Release: claim
6365553871(sessionsession_01UNcc5YAhWPpKE7Ttou4fqu, card thread ofdomain:specseat 1, seat sessionsession_013odhA26dvzy7pQmevdTTvy) · 2026-10-23T13:11Z · reason: premise partly disproved at the order's measurement point (os-dev-report6365644870,needs_decision, no PR, no write). →needs-user-decision, assignee cleared.维护者速读
问题: 这张卡原本要把按钮上的三段文字(确认提示、成功提示、失败提示)和两处"空状态"文字统一改成
{{ }}模板槽,和 #24798 一样。实测发现三件事:${record.name}这种写法,不是{{ }};平台自带的 4 个对象(邀请、成员、组织、用户)的成功提示就是这么写的。只改 spec 不改控制台,作者写的成功/失败提示会悄悄变成默认文案,空状态还会让页面崩。选项:
{{ }}模板,再把这 9 个槽连同已注册动作的两个同名槽、4 句平台文案作为一族统一过去,顺序和 [Decision] A bulk action'sconfirmText: refuse{{…}}holes by name (A), or declare{{count}}and the action's param holes and fill them (B)? HotCRM authors them in 12 strings (from objectui#13154) #24798 一样,拒收部分另出一张 BREAKING 卡。建议 A:同一个执行器键只留一种写法,是 ADR-0032 第 3 条的要求;但卡没有使用者拉动,若您更看重这一点就选 C。A 还是 C?
Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Ruling: letter A · maintainer chose 「A 先改控制台,再统一一族」 on the spec seat's decision card, recorded 2026-10-23T13:14Z
record:historyis literal by design). The card's count drops to 9 action-row slots.{{ }}holes (the renderer half of objectstack#24814, ruling A) objectui#13367 makes the sharedActionRunnerread the template envelope forconfirmText/successMessage/errorMessageand fill{{ }}holes, keeping${…}working through the transition. Then a.objectui-shabump.ActionSchema'sconfirmText/successMessageand the 4 platform-objects${result.*}sentences (converted), in the feat(spec,lint): a bulk action's confirmText is a template slot over count and its params, judged at validate, compile and the publish gate #24947 form: blank arm kept, door findings as warnings, and the judge taught to prescribe{{ }}for a${…}token. Widening only: minor,Clause-②: yes (widening).Blocked-by: objectstack-ai/objectui#13367
Generated by Claude Code
feat(core,react,app-shell): action confirmText, successMessage and er…
objectstack-fleet commentedon Oct 23, 2026
Still blocked, now on the install side · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-23T19:54ZRestart-when:
.objectui-shaonmainnames objectui8e90779dcb825a519c0c002b72d6f10b1d56819for a descendantBlocked-by:targets are closed: [Decision] A bulk action'sconfirmText: refuse{{…}}holes by name (A), or declare{{count}}and the action's param holes and fill them (B)? HotCRM authors them in 12 strings (from objectui#13154) #24798, and ActionRunner: confirmText / successMessage / errorMessage read the template envelope and fill{{ }}holes (the renderer half of objectstack#24814, ruling A) objectui#13367. The renderer half landed as objectui PR feat(plugin-security): extend the packaged-permission-set lock to the restore leg of the write-through #13409, merge8e90779dcb, at 2026-10-23T17:37Z..objectui-shaonmainis6fb31dcc9954. The compare8e90779dcb...6fb31dcc9954readsbehind: the pin predates the fix.main. If the spec typed these slots as templates before the pin moves, the pinned console would read the envelope as a non-string, as on 2026-10-23, when feat(spec,lint): a bulk action's confirmText is a template slot over count and its params, judged at validate, compile and the publish gate #24947 reddenedConsole Pin Gate(hourly full run: red on main (CI) #25438).pm:blocked. The next.objectui-shamove past8e90779dcb(one is due after the 18.1.0 cut) unlocks it. The unlock scan reads this restart condition.Generated by Claude Code
objectstack-fleet commentedon Oct 23, 2026
Unlocked: the pin condition is met · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-23T23:52ZRestart-when:met..objectui-shaonmainis2958f17e9c6a(PR chore(console): move the objectui pin 6fb31dcc9954 → 2958f17e9c6a, past objectui#13427, so the console builds against the spec's external role #25546, merged 2026-10-23T23:36Z). The compare8e90779dcb...2958f17e9c6areadsahead, so the shipping console now carries objectui PR feat(plugin-security): extend the packaged-permission-set lock to the restore leg of the write-through #13409's envelope reader.pm:blocked→pm:queue. The grade is unchanged at p3,domain:spec. The order is ruling A: the console first, then the family.Console Pin Gatered today when a spec type moved ahead of the pinned console. Either compile the pinned console against the branch, or land with the matching retirement registered.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim: PM loop round 1
Session:
session_01PC4NPXPgPmpDRG9DdDnyXTAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-24814-action-copy-template-slotsWorktree:
objectstack-issue-24814Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface:
packages/spec/src/ui/component.zod.ts(the 9 component action-row slotsconfirmText/successMessage/errorMessageat about:4036–:4038,:4136–:4138,:4286–:4288onorigin/maine649c2d91e),packages/spec/src/ui/action.zod.ts(ActionSchema.confirmText/successMessage), the template-slot judge and its prescription (the #24947 mechanism:TemplateExpressionInputSchema, the bound-root judge atvalidate/compile/ the publish gate) inpackages/specandpackages/lint, with door findings as warnings; adomain:serverrider the ruling names: the 4${result.*}success sentences inpackages/platform-objects(sys-invitation,sys-member,sys-organization,sys-user), converted; tests; regenerateduishards and references; one changeset.changeset/24814-action-copy-template-slots.md.emptyTextis out of scope by the ruling. (stop on breach; explain in the report)Container & model:
M,mode:cloud(surface ispackages/spec/src/**pluspackages/lintandpackages/platform-objects⇒ build-heavy, one card per container),model: default (TIER_DEFAULT; dispatch-gates --tier for this surface: no path-derived mandate),effort: high.Clause-②: yes⇒ aCONTRACT_REVIEW_TIERcontract review is owed before the queue.Clause-②: yes (widening)
Metadata-protocol: semantic
Responsibility: n/a — not a defect card
Ruling-ref:
6365736632(letter A, maintainer 「A 先改控制台,再统一一族」, read now on this card); unlock6377096620(.objectui-sha2958f17e9c6acarries objectui PR #13409's envelope reader)Thread-read: 6377096620
Serial constraints cleared: PR #25612 (#25511, this seat) edits the same file,
ui/component.zod.ts(therecord:detailshideEmptydescribe at about:1440, a disjoint region), and regenerates the sameuireference family. It is ACCEPTed (6382491996) and lands first. This card's dev mergesorigin/mainonce before opening its PR, and the generateduifamily lands one PR at a time throughscripts/pm/os-regen-merge.sh. Fold-or-serial: serial, not foldable (a different defect form, gate ①). No other open PR touchesui/component.zod.ts,ui/action.zod.ts, the template judge or thoseplatform-objectsfiles (all 13 open PRs' file lists re-read 2026-10-24T09:25Z); none of the openpm:dispatchedcards in other lanes declares these files orpackages/platform-objects/src/identity/sys-{invitation,member,organization,user}*.Premise: holds on
origin/maine649c2d91e— the 9 slots are stillz.string()at the lines above; the previous attempt's branchclaude/project-thread-j9tmar(925fd0772c, empty) was released at6365681003and is not reused.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim addendum (amends the file surface of
6382532929) ·domain:specseat 2 · 2026-10-24T09:26ZFile surface, added:
packages/spec/src/system/i18n-resolver.ts(the translatedconfirmText/successMessagereads, about:616) andpackages/cli/src/utils/i18n-extract.ts(domain:server), only as far as the slots' new type needs them, as the #24947 precedent did. Both files are also on this seat's in-flight #25459 (claim6381897923, the form-section functions there). They are disjoint regions and ordinary concurrency, not a single-writer path: whichever PR lands second mergesorigin/main.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Dispatched · round 1 ·
domain:specseat 2 · 2026-10-24T09:28Zmode:cloud, cloud sessionsession_01DcqhBfbrsH8ACDsMQ62Hzq(created by this seat's sessionsession_01PC4NPXPgPmpDRG9DdDnyXTthroughcreate_session, outcome branch as claim6382532929names). Liveness probes and any takeover read that session first; the claim stays the identity.6365644870as leads to re-measure, the unlock's downstream shape check (compile the pinned console against the branch), the consumer-package test list, theWrites:line.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{ "issue": 24814, "status": "done", "branch": "claude/issue-24814-action-copy-template-slots", "pr": "https://github.com/objectstack-ai/objectstack/pull/25637", "session": "session_01DcqhBfbrsH8ACDsMQ62Hzq", "premise_still_valid": true, "summary": "Typed the action-copy family as ADR-0032 template slots in the #24947 form (ruling 6365736632, letter A). That covers the 9 page action row slots (action:button / action:icon / the action:group-menu member x confirmText, successMessage, errorMessage) and ActionSchema.confirmText / successMessage, which InlineActionSchema picks and which keep the inline locale map arm. The blank arm is kept. The roots were measured at the pinned objectui ActionRunner (2958f17e9c6a): confirmText over record, plus params only beside static values; successMessage over result and record; errorMessage over none, because the runner shows it as written (re-measured: it interpolates nothing). They are exported from @objectstack/spec/ui. The closed-scope judge now reads ${...} with its $, so it prescribes {{ x }}, never ${{ x }}, and it names every hole in a zero-root slot as literal text. A new lint pass (validate-action-copy-templates.ts, inside validateStackExpressions) judges registered actions, page rows and inline properties.action at validate / compile / the publish gate. 'page' joins runtimeTypes, and every finding is a warning (an advisory, never a 422). The 4 named platform-objects ${result.email} sentences and 9 more plugin-approvals ${result.request.record_title} toasts were converted in every locale (see deviations). The i18n resolver and extractor read the envelope, and three expression-surface ledger rows classify the 11 new positions. The renderer half was re-verified at the pin, and the pinned console compiles against this spec.", "tests": "All at head e875a564 unless noted. packages/spec: new src/ui/action-copy-template.test.ts, plus 5 new judge cases in flow-text-slot-template.test.ts. Full suite at 8fed93a: 789 of 793 files passed, 3 failed, 1 skipped (Tests 3 failed, 22956 passed); all 3 fixed (action.test.ts, component-report-items-action-members-typed.pin.test.ts, export-origins artifact). After the restructure, src/ui + src/system + the judge: Test Files 211 passed (211), Tests 7614 passed. bulk-confirm-template, action-confirm-params-guard, component-props-unknown-members.pin and i18n-resolver* are inside that set and green. packages/lint: new validate-action-copy-templates.test.ts (13, real runRuntimeAuthoringRules on page and action writes: 1 advisory, 0 errors). Full suite: Test Files 159 passed (159), Tests 7250 passed (validate-expressions*, authoring-rule-wiring, runtime-gate* included). packages/platform-objects: 94/94 files, 1343 tests. plugin-approvals: 95/95, 1244. cli --project unit: 327/327, 4812 (i18n-bulk-action-coverage and the i18n tests included). metadata-protocol protocol.runtime-authoring-gate (197), rest meta-view-container-i18n (11), runtime action-execution-destructive (63), service-i18n authored-translations (16): all pass. dogfood expression-conformance.test.ts: 8/8, with 3 new ledger rows. Before they existed the census reported 11 UNCLASSIFIED positions, the proof the census sees them. NOT MEASURED: member-transfer-ownership-step-down.dogfood.test.ts (edited to read the served copy through templateSlotSource; it boots the showcase, so CI's battery runs it). Downstream shape check: OBJECTUI_ROOT=../objectui bash scripts/build-console.sh with objectui at 2958f17e9c6a against this branch's built spec (pre-merge head 3c1eb1e0). Turbo 36/36 + 34/34 successful, the console's 'tsc && vite build' completed, and the canary read '✓ Console bundle carries THIS tree's @objectstack/spec, and only it'. Ablation: isDollarToken forced to 'return false' via scripts/ablation-replace.mjs (anchor 1->0, blob e12c4385->38c36986) gave 4 failed / 41 passed in flow-text-slot-template.test.ts; restore put the blob back at e12c4385 with git diff HEAD empty. Producers measured at e649c2d91e: 13 authored action-copy sentences with ${ (4 platform-objects, 9 plugin-approvals) and 0 with {{. On HEAD: 0 with ${, 13 with {{. examples/**: 0 either way.", "gates": "Local, at e875a564. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 119 families (46 paths vs merge base 481ac6211). Each was run with its exit captured before any pipe. 115 exited 0 on the first pass. 4 exited 3, PREREQUISITE NOT MET: check-plugin-teardown-shape --self-test (a shallow clone; fetched the pinned fixture 621a4876), spec check:skill-examples, check:dual-build-cjs-loads and check:i18n-coverage (unbuilt packages). After turbo run build (77/77) all 4 exited 0. --ran with 'cmd :: exit N' answered: '119 derived famil(ies) accounted for — 119 run, 0 NOT-MEASURED (a DERIVED zero — all 119 recorded an exit code and none of them is 3)'. Extra: spec check:generated reported 'All 14 generated artifacts are up to date' (after --fix regenerated api-surface/ui.json, export-origins/ui.json and the ui/data/kernel reference pages, and after the merge regenerated component.mdx via spec build + gen:docs on the committed merge). typecheck exited 0 on spec, lint, cli, platform-objects, plugin-approvals and dogfood. check:dev-prereqs exited 0 and check:skill-frame-freshness exited 0. CI check-runs on #25637: in_progress at report time, not awaited per contract.", "dogfood_battery": "n/a — no `!` in the PR title and the Clause-② arm is (widening)", "mcp_calls": "0", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay, pr_create #25637 with assignee sunhaolin, read back byte-identical); POST /repos/objectstack-ai/objectstack/issues/24814/comments (this os-dev-report, via post-stamped)", "deviations": [ "plugin-approvals conversion beyond the ruling's '4': packages/plugins/plugin-approvals/src/sys-approval-request.object.ts carried 9 ${result.request.record_title} success toasts, which the earlier report's census missed. They were converted with their 3 translated bundles, the regenerated en bundle and 3 test pins. The four bounded in-place conditions were checked: same defect class; a mechanical conversion of a pinned form; no open PR touches the file (all open PRs' file lists read, only #25612 overlaps, on component.zod.ts); same gate family. Owed: the seat adds packages/plugins/plugin-approvals/src/sys-approval-request.object.ts and src/translations/* (plus the 2 test files) to the claim's file surface.", "Files outside the claimed surface, each needed by the slots' new type: packages/qa/dogfood/test/expression-conformance.ledger.ts (3 rows for the 11 new template positions) and member-transfer-ownership-step-down.dogfood.test.ts (reads the served copy through templateSlotSource); packages/cli/src/commands/explain.ts (confirmText row type); packages/plugins/plugin-approvals test pins; platform-objects translated-locale successMessage values (hand-edited, which AGENTS.md allows; no source-hash entries exist for them).", "The slot's template input is built at each declaring .zod.ts file (component.zod.ts actionRowCopy, action.zod.ts actionCopy) rather than inside ui/action-copy-template.ts. The expression-surface census scans only *.zod.ts, and a first cut in the plain module was invisible to it.", "Harness attribution reminder not followed in the PR body: the body carries only the AGENTS.md session-URL footer, per os-dev.md's precedence. Commit trailers use the model-free pair.", "Not subscribed to PR activity, per os-dev.md (the dispatch contract outranks the platform's auto-subscribe posture).", "No reverse type probe file was planted. The downstream console compile against the rebuilt dist stands as the cross-package type read." ], "files_changed": [ ".changeset/24814-action-copy-template-slots.md", "content/docs/references/data/object.mdx", "content/docs/references/kernel/metadata-plugin.mdx", "content/docs/references/ui/action.mdx", "content/docs/references/ui/component.mdx", "packages/cli/src/commands/explain.ts", "packages/cli/src/utils/i18n-extract.ts", "packages/lint/src/authoring-rules.ts", "packages/lint/src/runtime-gate.page-visibility-predicate-writes.test.ts", "packages/lint/src/validate-action-copy-templates.test.ts", "packages/lint/src/validate-action-copy-templates.ts", "packages/lint/src/validate-expressions.test.ts", "packages/lint/src/validate-expressions.ts", "packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts", "packages/platform-objects/src/identity/invite-user-action-copy.test.ts", "packages/platform-objects/src/identity/sys-{invitation,member,organization,user}.object.ts", "packages/platform-objects/src/identity/transfer-ownership-action-copy.test.ts", "packages/plugins/plugin-approvals/src/approval-service.test.ts", "packages/plugins/plugin-approvals/src/subject-title-reader-fls.integration.test.ts", "packages/plugins/plugin-approvals/src/sys-approval-request.object.ts", "packages/plugins/plugin-approvals/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts", "packages/plugins/plugin-approvals/src/translations/success-copy-names-request-i18n.test.ts", "packages/qa/dogfood/test/expression-conformance.ledger.ts", "packages/qa/dogfood/test/member-transfer-ownership-step-down.dogfood.test.ts", "packages/spec/api-surface/ui.json", "packages/spec/export-origins/ui.json", "packages/spec/src/automation/flow-text-slot-template.test.ts", "packages/spec/src/automation/flow-text-slot-template.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.test.ts", "packages/spec/src/ui/action.zod.ts", "packages/spec/src/ui/component-props-unknown-members.pin.test.ts", "packages/spec/src/ui/component-report-items-action-members-typed.pin.test.ts", "packages/spec/src/ui/component.zod.ts", "packages/spec/src/ui/index.ts" ], "open_questions": [ { "question": "ActionSchema.errorMessage and outcomeMessages: the ruling names only ActionSchema's confirmText / successMessage, so errorMessage stays an I18nLabel and outcomeMessages keeps the ${result.*} reading the runner applies to it. Bring them into the family?", "options": [ "A leave as is (this PR; follows the ruling's letter)", "B a follow-up card types ActionSchema.errorMessage over no roots and moves outcomeMessages to {{ result.* }} with a runner change first" ], "recommendation": "A for this PR, decided as an implementation trade-off within the ruling. B only if the maintainer wants one dialect across every runner copy key; outcomeMessages needs the renderer first, since the runner fills only ${…} there." }, { "question": "The ${…}-aware prescription is applied only in the closed-scope judge (closedScopeTemplateRefusal). The flow text slots' judge (textSlotTemplateRefusal) is unchanged. Extend it there too?", "options": [ "A closed scope only (this PR)", "B the flow judge as well" ], "recommendation": "A: the flow text slots never carried a ${…} dialect, and their refusal text is pinned. Changing it would widen scope with no producer pulling." }, { "question": "The refusal half (blank and door findings as errors) for this family, the analogue of #25205, is to be filed when this card lands per the ruling.", "options": [ "A the seat files it at landing" ], "recommendation": "A, as the ruling orders. It flips actionCopyTemplateIssues' severity and drops the ADVISORY suffix." } ], "out_of_scope_findings": [ "carrier: none · Studio's action inspector and preview (objectui ActionDefaultInspector / ActionPreview) read the three keys as I18nLabel maps and would show an envelope as 'template', as noted on objectui #13409. Authoring surface; noted in Acceptance notes, not filed · dedupe words: ActionDefaultInspector template envelope, ActionPreview confirmText envelope", "carrier: objectui #13183 · the expression-surface ledger row template-bulk-confirm-text still says no evaluator is found for the bulk confirmText; restate it when that renderer lands · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim addendum (amends the file surface of
6382532929/6382542433, this round) ·domain:specseat 2 · 2026-10-24T12:22ZFile surface, added, to cover the in-place extension PR #25637 declares (its body's Deviations section, the 13-producer census):
packages/plugins/plugin-approvals/src/sys-approval-request.object.ts,src/translations/*.objects.generated.ts, and the test pinsapproval-service.test.ts,subject-title-reader-fls.integration.test.ts,translations/success-copy-names-request-i18n.test.ts(domain:server): the 9${result.request.record_title}success toasts the earlier census missed, converted to the template spelling.packages/qa/dogfood/test/expression-conformance.ledger.ts(3 rows for the 11 new template positions) andmember-transfer-ownership-step-down.dogfood.test.ts(reads served copy throughtemplateSlotSource).packages/cli/src/commands/explain.ts(theconfirmTextrow type).The four in-place conditions, checked by this seat: the same defect class (action copy written in the
${…}dialect); a mechanical conversion of a pinned form; no other claim on those files (PR #25600 onplugin-approvalsmerged); the same gate family. Concurrency note: PR #25626 (plugin-auth, open, ready) also editsplatform-objects/src/identity/sys-user.object.tsand the fourplatform-objectslocale bundles. Different regions; whichever lands second mergesorigin/mainand regenerates bundles withcheck-i18n-bundles --write, never by hand.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
ACCEPT → PR #25637 comment 6384169307 ·
domain:specseat 2 · 2026-10-24T12:24Z · heade875a56445. ACONTRACT_REVIEW_TIERcontract review is owed before landing (needs:contract-reviewon the PR). At the merge, the seat files the refusal-half card the ruling orders.Generated by Claude Code