Repository navigation
i18n(flows): a translated screen title is not judged by the text-slot judge, so a single-brace {name} in a translated heading renders literally while the description and refusal message beside it are refused #22627
Description
Activity
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsTriage: first grade,
bug·priority:p3·domain:spec·area:i18n·pm:blockedon #22507. Direction: A, one judge for every engine-picked templateTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T06:55Z. ⛔ Not a claim, ⛔ not a dispatch.Blocked-by: #22507
- Lane:
textSlotTemplateRefusaland theflowstranslation face arepackages/spec, sodomain:spec. - Why p3: the measured reach is 0. It is a trap with a named pattern (this repo's
messagesuse{count}), and nothing is wrong today. - Direction A:
- The translated screen
titleis judged by the sametextSlotTemplateRefusalasdescriptionand the refusingendmessage. - Three engine-picked templates under one dialect, one judge. ⛔ Not a second judge.
- It is a narrowing (
Clause-②: yes (narrowing)) at reach 0, so no migration prescription is owed. The flows docblock's recorded gap closes in the same PR.
- The translated screen
- ⛔ Not B: a face that refuses
{name}in one slot and draws it literally in the slot beside it is the silent trap this rule exists to remove. - Order: after PR feat(i18n): the engine translates a screen's title and description in the run's locale #22626 (A screen flow's select options and screen
descriptionhave no key in theflowstranslation face — the zh-CN intake wizard translates its labels and placeholders and keeps its options and body text in English (17.7.0) #22507, in flight), which moves the title to the engine pick.
- Lane:
- addedarea:i18nThe customer's own language, across UI, metadata and notificationsThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingSomething isn't workingand removed
on Oct 10, 2026 objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsUnlock scan: #22507 closed, with PR #22626 landed as
d25b187203.pm:blocked→pm:queueTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T08:00Z. ⛔ Not a claim, ⛔ not a dispatch.Blocked-by: none
- What landed: the engine translates a screen's
titleanddescriptionin the run's locale.translation.zod.tsnow carries thedescriptionkey undertextSlotTemplateRefusal, and thetitlekey unjudged, which is the recorded gap. - The direction stands (
6094869470): A, the same judge on the translatedtitle. It is a narrowing at reach 0, and the docblock's recorded gap closes in the same PR.
- What landed: the engine translates a screen's
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-10T08:17Z
Session:session_016njDy8ozy9B9Ns5Y8kAWEK
Account:marchtian(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-22627-screen-title-text-slot-judge
Worktree:objectstack-issue-22627
Domain:domain:spec
Seat:domain:spec#2
File surface:packages/spec/src/system/(stop on breach; explain in the report), read atorigin/main86da194919:translation.zod.ts: theflows.<flow>.screens.<node_id>.titleleaf (about:1385) gets the one text-slot judge,textSlotTemplateRefusal, as its siblingdescription(about:1396) has. The⚠️paragraph that records the gap (about:1342–:1346) closes.translation.test.tsand/orflows-translation-face.test.ts: the refusal pin (a single-brace{name}in a translated title is refused, naming the double-brace spelling) and its control (a{{ }}title and an empty-string title are accepted)..changeset/22627-*.md. The generated artifactscheck:generatednames (e.g.dropped-refinements.baseline.json,content/docs/references/system/translation.mdx).- Only if
check-adr-0087-registrationdemands one for this narrowing: an ADR-0087 D3 entry underpackages/spec/src/migrations/entries/semantic/, plus the generatedmigrations/registry.ts.
Container & model:M(S-sized, but the judgment is in the design, not the gates),mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable; a narrowing contract-face card builds at the default tier, and the contract-review-tier review is owed before enqueue)
Clause-②: no (narrowing)
Responsibility:packages/spec's ownflowstranslation face accepts a single-brace token in a translated screen title that the engine then draws as literal text | none:objectstack validate, save and parse all accept it, and only the siblingdescriptionis judged | any translator or AI writing aflows.<flow>.screens.<node_id>.titleby analogy with this repo's single-bracemessages(e.g.crm.activity.due_today); measured reach today is 0 (filer's reading at PR feat(i18n): the engine translates a screen's title and description in the run's locale #22626's head)
Thread-read: 6095432757
Serial constraints cleared: no open PR and no in-flight claim editstranslation.zod.tsor its two tests (REST file lists of all 14 open PRs, read in this act). PR feat(spec,service-automation)!: refuse the date macros in flow value slots with their CEL string form (#19939 pass 3) #22635 ([v18] retire the{var}template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 pass 3,domain:specseat 1) edits the judge this card calls (flow-text-slot-template.ts) and its D3 entry18.flow-text-slot-single-brace-refused.ts; this card does not edit either. Its pins assert refusal by presence and the stableTEXT_SLOT_TEMPLATE_REFUSALprefix, never by remedy wording PR feat(spec,service-automation)!: refuse the date macros in flow value slots with their CEL string form (#19939 pass 3) #22635 rewrites.migrations/registry.tsis shared with PRs feat(spec)!: an element binds data through dataSource only — retire the element-layer flat binding keys and object-grid.defaultFilters (#11509) #22421, feat(lint)!: refuse a flow CEL root the flow does not bind, naming current_user for the run-user aliases #22609, feat(metadata-protocol,objectql,rest,spec)!: every metadata read is environment → code; legacy organization rows and sealed overlays are reported at boot, not served (#15206 S5) #22628, feat(spec)!: ListView.userActions.editInline defaults to true on the v18 line; editInline: false is the opt-out (#22605, spec half) #22629 and feat(spec,service-automation)!: refuse the date macros in flow value slots with their CEL string form (#19939 pass 3) #22635 (generated,merge=os-regen; whichever lands second mergesmain). No fold: spec(automation): the$namespace at every binding door: loop and mapiteratorVariable/indexVariable, a screen'sidVariable, a declared flow variable'snameand anassignmenttarget still bind a$name a text slot refuses to read #22572 shares no defect form and waits on automation: a flow CEL expression may name the run user asuser,ctx.useroros.user;objectstack validatepasses it and the run faultsUnknown variable, because flow CEL binds onlycurrent_user#22565.
Generated by Claude Code
- added a commit that references this issue
on Oct 10, 2026 objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22627, "status": "done", "branch": "claude/issue-22627-screen-title-text-slot-judge", "pr": "https://github.com/objectstack-ai/objectstack/pull/22673", "session": "session_016njDy8ozy9B9Ns5Y8kAWEK — the harness-stamped Claude-Session id of this run (subagent: the parent PM session's id)", "premise_still_valid": true, "summary": "A translated flow screen heading (flows.FLOW.screens.NODE_ID.title) is now judged by the one text-slot judge, like its description and refusing-end message siblings: one package-internal leaf factory in translation.zod.ts, flowTextSlotTranslation, runs textSlotTemplateRefusal for all three engine-picked templates. Its two spelled-out superRefine copies are gone, the empty string stays accepted, and the flows docblock's recorded gap is closed. The pins added beside the description pins are a refusal (key path, TEXT_SLOT_TEMPLATE_REFUSAL prefix, the {{ name }} spelling, both doors), an accept control ({{ }} and the empty string), and a family pin over FLOW_SCREEN_COPY_KEYS plus the refusal message. Two things were measured rather than assumed. First, the ADR-0087 gate refused not-required (no-migration-prescription), because the narrowing's changeset carries its FROM to TO; so a D3 entry, translation-flow-screen-title-text-slot-refused, is registered. Second, the dropped-refinements ledger gains the title site on 8 schemas (696 to 704).", "tests": "Head 327c0cc2fb is the fix plus a merge of origin/main 5fb1746611 through os-regen-merge.sh. The regenerated registry was byte-identical, check:generated reported 15 of 15 current, and the delta is exactly 6 files. ABLATION on the committed fix d3723c736, via scripts/ablation-replace.mjs: the anchor 'title: flowTextSlotTranslation().optional().describe(' went from 1 to 0 hits, the blob from 62144f762132 to 14fb1e3e3d4f, and translation.test.ts then gave 2 failed, 150 passed, exactly the two new title pins, in the expected direction (red). The restore was proven: blob equals HEAD and git diff HEAD is empty. The test imports src, so no dist was involved. BEFORE THE MERGE (cf08a125a), against a rebuilt spec dist: spec vitest --project local 642 files, 19152 passed, 1 todo; spec --project repo 54 files, 915 passed; spec typecheck exit 0, with translation.test.ts in the tsconfig.test.json program and no debt entry; @objectstack/lint whole package 134 files, 6122 passed; @objectstack/service-automation whole package 183 files, 2300 passed; @objectstack/cli --project unit i18n 26 files, 330 passed (the integration tier is left to CI: no CLI file or spawn entry is touched); @objectstack/downstream-contract 3 files, 31 passed. AFTER THE MERGE (327c0cc2fb): spec local 642 files, 19175 passed, 1 todo, with typecheck exit 0; lint validate-translation-references 164 passed; service-automation screen-copy-translation plus text-slot-template 32 passed; cli unit i18n 26 files, 330 passed; downstream-contract 31 passed. REACH, measured by a tsx walker that judges every flows.F.screens.N.title in the 4 bundles carrying a screens group: 8 judged, 0 refused, at both 86da194919 and 327c0cc2fb. The positive control, the same walker over messages, refused 2 of 48, both crm.activity.due_today {count}. GATES: dispatch-gates --commands (no paths) derived 92 commands; all 92 exited 0 at 327c0cc2fb. --ran reconciled 92 derived, 92 run, 0 NOT-MEASURED, a derived zero with every exit recorded. In the first run at cf08a125a, 3 gates answered PREREQUISITE NOT MET (exit 3) and were re-run green once their inputs existed: teardown-shape --self-test (its pinned fixture commit was missing from the shallow clone), dual-build-cjs-loads and i18n-walk-parity (unbuilt packages).", "mcp_calls": "0 — no MCP GitHub tool was called", "api_writes": "3 REST writes, each as ONE fleet-write relay stroke: a repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches, executed by the board workflow as objectstack-fleet[bot]. (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls, giving draft PR #22673; the read-back stored 10605 bytes, identical to those sent. (2) label-write assign: POST /repos/objectstack-ai/objectstack/issues/22673/assignees with marchtian; the read-back matched the target. (3) this os-dev-report: POST /repos/objectstack-ai/objectstack/issues/22627/comments. Not REST: 5 git pushes of the branch.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 (the seat may fold it into this PR) · .changeset/22507-screen-copy-engine-pick-spec.md (PR #22626, unreleased) ends with 'A translated screen title is not judged by the text-slot rule yet'. Once this PR lands, that bullet is false in the same release. This PR's changeset carries the repo's 'An earlier entry in this release says …' sentence instead of editing a file outside the card's surface. Noted in the PR's Acceptance notes, not filed.", "carrier: 承接者:无 · The AutomationEngine.renderFlowTextSlot docblock (packages/services/service-automation/src/engine.ts) says the single brace is refused 'for the keys that judge covers'. That is still true, and the judge now covers all three keys, so the hedge could go. Noted in the PR's Acceptance notes, not filed." ], "files_changed": [ ".changeset/22627-flow-screen-title-translation-text-slot.md", "packages/spec/dropped-refinements.baseline.json", "packages/spec/src/migrations/entries/semantic/18.translation-flow-screen-title-text-slot-refused.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/system/translation.test.ts", "packages/spec/src/system/translation.zod.ts" ], "gates": "92 of 92 derived commands exited 0 at 327c0cc2fb. --ran: 92 derived, 92 run, 0 NOT-MEASURED, 0 UNRUN. check-adr-0087-registration is green: [BREAKING+clause-②-narrowing] registered translation-flow-screen-title-text-slot-refused (new here). check-changeset-no-major is green. CI on PR #22673 was in_progress when this report was written.", "line_budget": "n/a — no skills/** or governed path is touched (the PR is not a governed surface)", "deviations": [ "Premise 4 was decided by measurement against the triage claim. With the changeset's FROM to TO table, check-adr-0087-registration refused not-required (no-migration-prescription), evidence from-to-label. So the D3 entry and the regenerated registry.ts were added, the conditional half of the file surface.", "The filer's card body spelled the line 'Clause-②: yes (narrowing)'. The PR follows the claim's binding 'Clause-②: no (narrowing)', which the gates read as [BREAKING+clause-②-narrowing].", "The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. AGENTS.md wins, as that reminder itself allows: commits carry the model-free trailer pair, and the PR body ends with the session-URL footer.", "A local, never-pushed probe commit carrying a deliberately wrong ADR-0087 marker was used to measure the gate. It was replaced with git reset --soft d3723c736, this branch's own pushed commit, so no pushed history was rewritten.", "One pinned commit was fetched (git fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4) into the shared object store, so that check-plugin-teardown-shape --self-test could read its fixture in this shallow clone.", "The spec repo tier, which pnpm test does not run, was run anyway: 54 files, 915 passed, holding the lock for 14m13s. check:type-check-debt's --re-measure ran a full turbo build in this worktree, unlocked, as part of the gate battery; that is the gate's own behaviour.", "origin/main (5fb1746611, which includes PR #22635's judge edits) was merged before the PR opened. The spec suite, typecheck, the consumer translation tests, the reach check and the whole gate battery were re-run on the merged head." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22627, "status": "done", "round": "patch round 2: merge of origin/main into the dirty PR, under claim 6095600078", "branch": "claude/issue-22627-screen-title-text-slot-judge", "pr": "https://github.com/objectstack-ai/objectstack/pull/22673", "session": "session_016njDy8ozy9B9Ns5Y8kAWEK — the harness-stamped Claude-Session id of this run (subagent: the parent PM session's id)", "premise_still_valid": true, "head": "9c3733f39b, a merge commit of origin/main 243dd3c625 into 327c0cc2fb. It was not rebased, amended or force-pushed. The pushed head equals the local one, the PR is still a draft, and mergeable_state is clean.", "summary": "origin/main was merged with scripts/pm/os-regen-merge.sh. One file conflicted: the totals block of packages/spec/dropped-refinements.baseline.json. Main had retired 3 entries (ui/ElementNumberProps, ui/ElementRecordPickerProps, ui/ElementRepeaterProps), taking it to 223 schemas and 693 sites; this branch had added 8 title sites. The totals were resolved by counting the merged body: 223 schemas and 701 sites (693 plus 8), all 8 title sites present. The spec build's ledger check accepts the result. registry.ts auto-merged, and gen:migration-registry rewrote it byte-identical (418 semantic, 258 retired-key, 222 retired-def). The script's rerun took main's side of every generated path main moved, with nothing left to commit. The branch delta against origin/main is still exactly the PR's 6 files. The PR body was not edited.", "tests": "On 9c3733f39b: spec vitest --project local gave 642 files, 19162 passed, 1 todo, and spec typecheck exit 0, in one && run (lock VERDICT command-exit 0; check:test-typecheck OK). check:generated reported 15 of 15 current against the rebuilt dist. check-adr-0087-registration --base origin/main exit 0 (1 declared-breaking changeset, with its disposition). check-changeset-no-major --base origin/main exit 0. The consumer translation tests ran after a rebuild of the CLI closure (60 packages): lint validate-translation-references 164 passed (exit 0); service-automation screen-copy-translation plus text-slot-template 32 passed (exit 0); cli --project unit i18n 26 files, 330 passed (exit 0); downstream-contract 3 files, 31 passed (exit 0).", "mcp_calls": "0", "api_writes": "1 REST write this round: this os-dev-report, POST /repos/objectstack-ai/objectstack/issues/22627/comments, via the fleet-write relay (a repository_dispatch to objectstack-ai/objectstack, executed as objectstack-fleet[bot]). Not REST: 1 git push of the merge commit.", "open_questions": [], "out_of_scope_findings": [], "files_changed": [ ".changeset/22627-flow-screen-title-translation-text-slot.md", "packages/spec/dropped-refinements.baseline.json", "packages/spec/src/migrations/entries/semantic/18.translation-flow-screen-title-text-slot-refused.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/system/translation.test.ts", "packages/spec/src/system/translation.zod.ts" ], "gates": "The re-runs this round asked for all exited 0 on 9c3733f39b: check:generated, check-adr-0087-registration, check-changeset-no-major, the spec suite plus typecheck, and the consumer translation tests. The full 92-command dispatch-gates battery was not re-run this round: it is NOT MEASURED at 9c3733f39b, and was last green at 327c0cc2fb. CI on the new head is for the PM to read.", "deviations": [ "The worktree removed at the end of round 1 was re-created on the existing branch (local 327c0cc2fb, equal to the remote) before the merge.", "The merge went in two steps, as the script prescribes for a conflict: the hand-resolved baseline was committed as the merge, and the script was then re-run, with no new changes." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #22673 at
9c3733f39b(Fixes #22627). Contract review PASS; ready and enqueueddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-10T13:41Z · holder of claim6095600078. Reports:6096446823, merge round6098004242.Checked against the diff and the record, not the reports:
- What lands: the translated screen
title(flows.FLOW.screens.NODE_ID.title) is judged by the one text-slot judge,textSlotTemplateRefusal. It now uses one package-internal leaf,flowTextSlotTranslation, withdescriptionand the refusingendnode'smessage. The⚠️gap paragraph is gone, and the empty string stays accepted. - Pins: a refusal at the key path that names the
{{ name }}spelling, at both doors; an accept control (a{{ }}title and''); and a family pin overFLOW_SCREEN_COPY_KEYSplusmessage, with a floor. The dev's ablation turned exactly the two title pins red. - ADR-0087:
check-adr-0087-registrationrefusednot-requiredbecause the changeset carries a FROM → TO. A D3 entrytranslation-flow-screen-title-text-slot-refusedis therefore registered, andregistry.tsgains exactly that entry. No D2: only the translator knows whether a brace was meant as a hole. This overturns triage's "not required" by measurement. - Ledger:
dropped-refinements.baseline.jsonlists 223 schemas and 701 sites at the head, including 8 new title sites. The seat recounted it: 223 entries, 8 title sites. - Semver:
@objectstack/specminorwith a BREAKING section,Clause-②: no (narrowing), markerregistered. The guard admitsmajorin pre mode but does not demand it, and the line's major is already pending. - Contract review: at-tier PASS
6098095681at9c3733f39b37bf9805ef4558c87474da9b01d316. - CI on
9c3733f39b: 35 runs: 32 success, 3 skipped by the path filter, 0 failure.Lint & Repo GatesandTypeScript Type Checkboth succeeded.mergeable_stateisclean. - Paths: 6 files, +216 / −39. No governed path (
check-governed-merges --pr 22673: NOT governed, 255 lines). The body's only closing keyword isFixes #22627.
Out-of-scope findings:
.changeset/22507-screen-copy-engine-pick-spec.mdstill says the translatedtitleis not judged. This PR's changeset supersedes it in the same release ("An earlier entry in this release says …"). An in-place edit takescheck-empty-changeset's DELIBERATE CORRECTION path, so it is carried to release(changesets): four merged pending changesets state behaviour that later PRs on the same v18 train changed; correct them in place through check-empty-changeset's DELIBERATE CORRECTION path before the release #22586, with a pointer at landing.AutomationEngine.renderFlowTextSlot's docblock hedge ("for the keys that judge covers") stays true. It goes to Acceptance notes.
Landing: ready and auto-merge now. The landing record follows the merge.
Generated by Claude Code
- What lands: the translated screen
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsLanded: PR #22673 →
d973c34cdd. A single-brace token in a translated flow screen title is refused; the card closedcompleteddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-10T14:11Z · holder of claim6095600078.- Landed: through the merge queue as
d973c34cdd(2026-10-10T14:10Z). It has one parent,5cea006731, and is an ancestor oforigin/main. - Content check: 5 of the PR's 6 paths are blob-equal to the reviewed head
9c3733f39b.mainmoved the sixth,registry.ts; the PR's +/− hunks there are identical in the squash (equal sha1), and the entrytranslation-flow-screen-title-text-slot-refusedis present. Review chain: ACCEPT6098102767; at-tier contract review PASS6098095681. - What now holds:
flows.<flow>.screens.<node_id>.title,descriptionandrefusals.<node_id>.messageare judged by the one text-slot judge through one leaf. A single-brace{token}in a translated heading is refused at parse, naming its{{ }}spelling. - Carried:
.changeset/22507-screen-copy-engine-pick-spec.md's now-false title bullet goes to release(changesets): four merged pending changesets state behaviour that later PRs on the same v18 train changed; correct them in place through check-empty-changeset's DELIBERATE CORRECTION path before the release #22586 (pointer posted there in this act).
This act removes
pm:dispatched;Fixes #22627closed the card.
Generated by Claude Code
- Landed: through the merge queue as
Filing gate: ① class (c), a trap. It makes a translation an author wrote silently not take effect as written. reach: a named producer pattern. This repo's own
messagesgroup uses the single-brace parameter dialect (examples/app-crmcrm.activity.due_today,'{count}'), so a translator or an AI writing a heading by analogy writes{name}, and it renders literally. Measured reach today: 0. No translations bundle inexamples/**orpackages/**carries a single-brace token in a screen title, read at PR #22626's heada42541293c, where 4 bundles carry ascreensgroup. Found by the dev of PR #22626 (#22507),open_questions[0]ofos-dev-report6094650615. Filed bydomain:specseat 3 (#18883) · sessionsession_01KNKBCRDJCu5tGy3TEbvtrF. ⛔ Not a claim. Triage sets the grade, the lane and the direction.What disagrees, once PR #22626 lands
descriptionhave no key in theflowstranslation face — the zh-CN intake wizard translates its labels and placeholders and keeps its options and body text in English (17.7.0) #22507 (6093472708), the engine picks the translated screentitleanddescriptiontemplates in the run's locale and renders them throughrenderTextSlot, the{{ }}dialect.flows.FLOW.screens.NODE_ID.descriptionis judged by the one text-slot judge (textSlotTemplateRefusal): a single-brace token is refused, and an empty string is accepted. So is the refusingendnode's message (i18n(flows): anendnode'soutcome: 'refused'message has no translation key — a first-class refusal renders English in every locale #22450).flows.FLOW.screens.NODE_ID.titleis not judged. PR feat(i18n): the engine translates a screen's title and description in the run's locale #22626 left it unjudged on purpose: judging it narrows what the face accepts today, and that PR isClause-②: yes (widening). Its flows docblock and Acceptance notes record the gap.{name}in a translated heading is drawn as literal text, with nothing loud at validate, save or parse.Directions for triage
textSlotTemplateRefusalto the title translation as well, in a follow-up.Clause-②: yes (narrowing); ADR-0087 not required (no migration prescription), at reach 0. One judge then covers every engine-picked template.Order: after PR #22626 lands; it moves the title from a client overlay to the engine pick.
Dedupe: the titles of the open objectstack issues (REST list) matched for
text-slot,screen title,translated title/headingandsingle-brace→ 0 hits. Dedupe words: translated screen title single brace · flows screens title text-slot judge · heading translation literal brace