Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(app-shell/inspectors,i18n): the dashboard widget inspector's field combo shows the English literal Search fields… as its search placeholder — engine.form.searchFields exists in en-US and zh-CN and its sibling inspector reads it #10696

Closed

Description

@objectstack-fleet

Filing-gate category: ① a product defect with a named site (class a), read from source with a lit control. Reader: triage first (route and grade), then the seat that claims it.

Filed by the domain:ui seat 5 (session_01KUxVUa7e39aNjhkKi1gsoy) from the at-tier contract review of PR objectui#10678 (objectui#10586, merged as d02974d), which named it as out of scope. The seat re-read it on objectui origin/main 7afc81d. ⛔ Filed bare, not graded here.

Fact

  • packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx:442 passes searchPlaceholder="Search fields…" to InspectorComboField as a string literal.
  • Every other label in the same inspector goes through t('engine.inspector.…', locale). Its dataset combo's placeholder does too (:339-340).
  • packages/app-shell/src/views/metadata-admin/i18n.ts already carries engine.form.searchFields: 'Search fields…' in en-US (:1319) and '搜索字段…' in zh-CN (:3594).
  • ⇒ A zh-CN author opening a dashboard widget's field picker reads the English Search fields… on an otherwise translated inspector.

Lit control: DatasetDefaultInspector.tsx:579 and :633 pass searchPlaceholder={tr('engine.form.searchFields')} to the same combo, and render 搜索字段… under zh-CN.

Direction (for triage, not a ruling)

Read the existing engine.form.searchFields key there, the way DatasetDefaultInspector does. No new key is needed, which keeps the objectui#4662 engine.* carve-out (en-US and zh-CN only) untouched.

Dedupe

Searched this repo with the MCP semantic issue search, including closed issues: DashboardWidgetInspector Search fields English placeholder untranslated ⇒ objectui#9652 (closed; the designer's unknown-value markers, a different literal), objectui#4748 (closed; WidgetConfigPanel in plugin-dashboard, a different surface) and older closed i18n cards. None covers this literal, and the hits show the instrument is lit.

Dedupe words: DashboardWidgetInspector Search fields English · widget inspector searchPlaceholder literal · engine.form.searchFields unread

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: the customer's language | i18n.surface-matrix | P2

    Triage: first grade (family closure card) — bug · priority:p3 · domain:ui · area:i18n · pm:queue (finding removed — graded)

    Triage: lands in packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx (the field combo's searchPlaceholder is the English literal Search fields…) ⇒ domain:ui; rationale: a zh-CN author reads English on an otherwise translated inspector, although the key engine.form.searchFields exists in both locales and the sibling DatasetDefaultInspector reads it. It was read from source with a lit control and has no measured reach: ⇒ p3, the designer hard-coded-English family (objectui#9652, p3). objectui#10586, that family's last card, is closed, so this card is its closure card for any string the census missed.

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01CRZSc7dU8oDStbTbSwhuZe · 2026-09-25T19:39Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its full thread, and objectui origin/main.

    Execution note: first, measure under zh-CN. Then read the existing engine.form.searchFields key here (no new key, so the objectui#4662 engine.* carve-out stays untouched), and census the inspectors for any other literal passed where a key exists.

  2. added
    area:i18nThe customer's own language, across UI, metadata and notifications
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 25, 2026
  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-10696-widget-search-placeholder
    Worktree: objectui-issue-10696
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx (the field combo's searchPlaceholder reads engine.form.searchFields), plus any other inspector the census (triage execution note) finds passing an English literal where an engine.* key already exists; tests; one changeset ('@object-ui/app-shell': patch). ⛔ No new i18n key (the objectui#4662 engine.* carve-out stays untouched). Stop on breach and explain it in the report
    Container & model: S, mode:subagent, model: opus — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes triage 5838471955 (bug · p3, family closure card: measure under zh-CN first, read the existing key, census the inspectors)
    Clause-②: no — a translated string replaces an English literal; no declared surface moves
    Thread-read: 5838471955
    Serial constraints cleared: open-PR file lists read 2026-09-25T20:14Z: no open PR touches metadata-admin/inspectors/ or metadata-admin/i18n.ts. check-governed-queue-guard.mjs --test packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx: NOT GOVERNED.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Release: domain:ui seat 2, session session_014mXUNuFomfj24w7s1pZzhN — shift closed on the maintainer's instruction (「继续,当前任务处理完就下班」, this session's chat, 2026-09-27); the dispatched os-dev stopped on a usage limit (weekly; the API reported the reset as 2026-10-01 21:00 UTC) before reporting ⇒ assignee cleared, pm:dispatched → pm:queue; the next holder claims it afresh.

    State at release (read from GitHub and the dev's worktree):

    • Branch claude/issue-10696-widget-search-placeholder is pushed at 68eedfed1 (1 commit: inspector literals with an existing catalogue row read it, including DashboardWidgetInspector's search placeholder and ViewVariantInspector.tsx, plus inspectorStrings.i18n-10696.test.tsx; 6 files, +415/-8). Worktree clean. No PR was opened and no report exists.
    • The dev's last stated step was the pin file, so the red-on-base / green-on-head evidence and the gates are unmeasured.

    What the next holder owes: verify the pin (red on base, green on head, per-site ablation), the census table, the gates, then open the draft PR (Fixes #10696, Clause-②: no) and review it.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 6 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-10696-widget-search-placeholder
    Worktree: objectui-issue-10696
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx (the field combo's searchPlaceholder reads engine.form.searchFields), plus any other inspector the census (triage execution note) finds passing an English literal where an engine.* key already exists; tests; one changeset ('@object-ui/app-shell': patch). ⛔ No new i18n key (the objectui#4662 engine.* carve-out stays untouched). Stop on breach and explain it in the report
    Container & model: S, mode:subagent, model: fable (the ceiling; opus subagents hit a weekly usage limit) — dispatch-gates --tier: 「no path-derived mandate … floor sonnet · default opus · ceiling fable」; executes triage 5838471955 (bug · p3, family closure card: measure under zh-CN first, read the existing key, census the inspectors)
    Clause-②: no — a translated string replaces an English literal; no declared surface moves
    Thread-read: 5851632349
    Serial constraints cleared: open-PR file lists read 2026-09-25T20:14Z: no open PR touches metadata-admin/inspectors/ or metadata-admin/i18n.ts. check-governed-queue-guard.mjs --test packages/app-shell/src/views/metadata-admin/inspectors/DashboardWidgetInspector.tsx: NOT GOVERNED.

    Re-claim: this seat's own Release: (5851632349) is withdrawn on the maintainer's instruction 「你应该把你当前的pr 都处理完,跟进到合并」 (this session's chat, 2026-09-27). The session stays on this card until its PR merges; the seat itself stays vacant for new work.


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 10696,
      "status": "done",
      "branch": "claude/issue-10696-widget-search-placeholder",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10741",
      "session": "session_014mXUNuFomfj24w7s1pZzhN",
      "premise_still_valid": true,
      "summary": "Resumed the branch at 68eedfed1 (1 commit, 6 files: DashboardWidgetInspector, ViewVariantInspector, ObjectFieldInspector, FlowEdgeInspector, the pin inspectorStrings.i18n-10696.test.tsx, one app-shell patch changeset) and took nothing as verified. H1 measured on base 383a34f52 under zh-CN: the widget inspector's filter-binding combo search box rendered `Search fields…` while DatasetDefaultInspector's combo rendered `搜索字段…`. H3 census (AST walk over the 27 inspector sources, default and broad passes, base vs head, each literal matched byte-for-byte against the EN catalogue) confirms exactly seven sites had a literal whose `engine.*` row already existed, all now reading the row: searchFields, searchObjects, removeNamed, valuePlaceholder (lookup and summary), branchCustom, branchN; no key added. Every remaining English literal is either a code sample / stored value (the family's ruling) or has no row of the same value; those are tabled in the PR body for the seat. Draft PR #10741 opened through the fleet relay (Fixes #10696, Clause-②: no); no code change was needed on top of 68eedfed1. The card's assignee at claim was os-elon-musk (untouched); the PR assignee write was refused by the classifier (see deviations).",
      "tests": "Head 68eedfed1, heavy runs through os-verify-lock (slot issue-10696). Pin `pnpm exec vitest run packages/app-shell/src/views/metadata-admin/inspectors/inspectorStrings.i18n-10696.test.tsx`: head `Tests 17 passed (17)` VERDICT command-exit 0. Base leg (four inspector sources checked out from 383a34f52, pin from head; restore proven by git hash-object == HEAD blob per path and `git diff HEAD` 0 lines): `Tests 7 failed | 10 passed (17)`, run exit 1 — the seven zh cases (`Received: \"Search fields…\"`, `\"Search objects…\"`, `\"Remove region\"`, `\"value\"` x2, `\"— Custom —\"`, `\"Branch 1 · record.amount > 100\"`), en cases and lit controls green. Per-site ablation via objectstack scripts/ablation-replace.mjs (anchor declared to hit once; mutation and restore judged by blob hash on disk): S1..S7 each `Tests 1 failed | 16 passed (17)`, failing only its own zh case, each restored to the HEAD blob. Two recorded no-ops: the driver's first call ran the tool outside the repo (refused before writing, hashes unchanged), and the first S2 reinstated the wrong text at the caller (ReferenceError, zh+en both red — invalid, redone inside ViewObjectPicker). Type-check: `tsc --noEmit` exit 0 and `tsc -p tsconfig.test.json` exit 0 in packages/app-shell (run directly; the turbo type-check was a cache hit), `--listFiles` names the pin once. Tests: metadata-admin/inspectors 103 files `Tests 1428 passed | 1 skipped (1429)` exit 0; the other 210 metadata-admin files in two halves `881 passed` and `1287 passed`, exit 0 each (313 = the tracked population); views/studio-design 65 files `359 passed` exit 0; the rest of app-shell and the repo-wide lint/test shards declared to CI (the four inspectors are reached only through inspectors/index.ts and the registry; exports unchanged). eslint plain form on the 5 changed TS files: `--format json` 5 files, 0 errors, 26 warnings, per file equal to base via `--stdin` on the base blobs (11/2/11/2, pre-existing no-explicit-any + one preserve-manual-memoization), new pin 0; eslint.config.js has no type-aware linting (grep exit 1). Gates exit 0 with their own verdict lines: check-changeset-presence (5 source files of 1 released package, 1 changeset), changeset:check, check:new-line-citations (0 new), check:control-bytes (OK), check:i18n-keys, check:i18n-drift (0 en values changed), check:i18n-dead-keys, check:i18n-designer-parity, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:pending-changeset-literals, check:changeset-claims (no pending changeset names a touched file). H5: 10586-inspector-strings-i18n.md and -2.md sentences still hold, no dated note, frontmatter untouched. Serial: origin/main c3a26ccdaa fetched into refs/worktree/upstream-main-10696, touches none of my files since base, `git merge-tree --write-tree` exit 0; not merged. NOT MEASURED: check:eager-locale-catalogues and the eager-closure budget (need a built apps/console bundle).",
      "mcp_calls": "0 — none",
      "api_writes": "1 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write relay, op pr_create, draft forced; run 36290151106 success) which executed POST /repos/objectstack-ai/objectui/pulls (PR #10741). Refused before any request: label-write --assign os-elon-musk on PR 10741 (see deviations). This os-dev-report comment through post-stamped is the only other write attempted; its outcome is in the final message. REST reads only: GET issue 10696, its comments, pulls/10678 (+files), pulls?head=… .",
      "deviations": [
        "PR assignee not set: `node scripts/pm/label-write.mjs --repo objectstack-ai/objectui --issue 10741 --assign os-elon-musk` was refused by the Claude Code auto-mode classifier before running (reason given: External System Writes). Not retried, not rerouted; the seat applies it. Its --dry-run had read the route as dispatch (relay live).",
        "H3 table column: the order asked for file:line; AGENTS.md #11 bans a line address in a PR body, so the PR body cites file · enclosing symbol/prop instead.",
        "Worktree /home/user/objectui-issue-10696 removed after the PR opened (node_modules deleted, `git worktree remove` exit 0 on a clean tree), per the role file's cleanup step; a patch round needs a fresh worktree.",
        "The turbo type-check reported a cache hit; both tsc projects were run directly and their exits echoed individually, which is the reading reported."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: none measured (read from source; the pin harness is the lit instrument) · designer inspectors still render English under zh-CN where NO catalogue row of the same value exists — this card forbids new keys, so listed, not fixed: DashboardWidgetInspector WIDGET_TYPES labels (KPI Metric, Bar Chart, …, Pivot Table) and InspectorEmpty messages; the untitled-widget fallback `Widget N` (3 sites); ObjectFieldInspector LOOKUP_OPERATORS labels (partial rows engine.inspector.condition.op.* exist without the symbol prefix, none for at least / at most / contains / in / not in); FlowEdgeInspector branch suffix ` · default`; FlowNodeInspector aria-label `nested node location` and the `Must be a JSON object` error shown via setAdvError; VariableTextInput PICK_LABEL / SEARCH_LABEL / EMPTY_LABEL · dedupe words: designer inspector English literal no key zh-CN · widget type labels untranslated · lookup operator labels untranslated · VariableTextInput Insert a reference · carrier: the seat, as this family-closure card's remainder (needs keys; objectui#4662 carve-out decision)"
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 10696,
      "status": "done",
      "branch": "claude/issue-10696-widget-search-placeholder",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10741",
      "session": "session_014mXUNuFomfj24w7s1pZzhN",
      "premise_still_valid": true,
      "summary": "Round 2 on PR #10741 after the contract review of 68eedfed1 (PR comment 5852290145) FAILED on one missed site. Head is now 7e724988e3 (pushed; 7 files, +174 -23 over 68eedfed1). The FlowNodeInspector nested-node breadcrumb's region crumb (Try / Catch / Branch N from flow-nested-selection.ts regionLabelOf) now reads engine.flowRegion.try / .catch / .branchN through displayRegionLabel — the canvas region header's own reader — so the canvas and the inspector show one word under zh-CN; Body and an authored branch name pass through; en byte-identical. Shape chosen: carry regionKey out of locateFlowNode (the key carries the branch index) and translate at the render site with the locale FlowNodeInspector already holds, rather than threading a locale into the pure codec (which stays English and i18n-free; its own test pins that it mirrors the canvas). displayRegionLabel itself is reused: moved verbatim from previews/flow-region-view.tsx into the leaf module previews/flow-region-label.ts (only runtime import ../i18n.js — no cycle, no new react-refresh warning), imported by both FlowRegionView and FlowNodeInspector, so the mapping is stated once. Census re-run over all 44 inspector sources (27 .tsx + 17 .ts): default pass 283 of 1276 candidates kept on head (289 of 1282 on base), broad pass 1982 of 3814 (1989 of 3821); the only base-minus-head rows are round 1's seven literals; the .ts helpers' remaining English is either another channel (flow-node-config.ts via FLOW_FIELD_ZH), stored values/type tokens, or the seat's list (Body, useFlowScope group headings, expression-validate messages). Changeset title now 'eight inspector words' with a breadcrumb bullet naming the canvas header as the rows' existing reader. No i18n.ts key added; no PR body PATCH — the full corrected body is in pr_body_replacement.",
      "tests": "Head 7e724988e3, lock slot issue-10696. Closure build `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' run build` VERDICT command-exit 0 (the recreated worktree had no dist/, so the first tsc read was TS2307 noise and is not reported). tsc --noEmit exit 0 and tsc -p tsconfig.test.json exit 0 in packages/app-shell (each exit echoed), --listFiles names the pin once. `pnpm exec vitest run` pin + FlowNodeInspector.test.tsx + flow-nested-selection.test.ts + FlowCanvas.test.tsx + flow-canvas-layout.test.ts + flow-region-metrics.test.ts: `Test Files 6 passed (6)`, `Tests 99 passed (99)`, exit 0 (the pin now 22 cases). Base leg (six inspector-side sources from 383a34f52 — the four round-1 inspectors + FlowNodeInspector.tsx + flow-nested-selection.ts — pin from head; each mutated path hashed to its BASE blob, restored to its HEAD blob, `git diff HEAD` 0 lines): `Tests 10 failed | 12 passed (22)`, exit 1 — the seven round-1 zh cases plus the three crumb cases (`Received: \"Try\"`, `\"Catch\"`, `\"Branch 2\"` against 尝试 / 捕获 / 分支 2); en cases and lit controls green; the existing en crumb test in FlowNodeInspector.test.tsx is the outside control (green on head). Ablation S8 via objectstack scripts/ablation-replace.mjs (anchor hit once, blob 7acd18b100ea -> f5ba42c018de -> restored == HEAD): crumb back to `{loc?.regionLabel}` gives `Tests 3 failed | 19 passed (22)`, exactly the three zh crumb cases. Gates exit 0 with verdict lines: check-changeset-presence (10 source files of 1 released package, 1 changeset), check:changeset-claims --base 383a34f52 --head HEAD (no pending changeset names a touched file), changeset:check, check:new-line-citations (0 new), check:control-bytes (OK, 8868 files), check:i18n-keys, check:i18n-drift (0 en values changed), check:i18n-dead-keys, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots. eslint --format json on the 6 round-2 TS files: 0 errors, 3 warnings (FlowNodeInspector.tsx's pre-existing no-explicit-any + react-hooks/set-state-in-effect, equal to its 68eedfed1 blob via --stdin); the new module, codec, its test and the pin 0; eslint.config.js has no type-aware linting. Serial: origin/main 1fc77fd806 fetched into refs/worktree/upstream-main-10696, touches none of my files since base, `git merge-tree --write-tree` exit 0; not merged. NOT MEASURED: check:eager-locale-catalogues and the eager-closure budget (built apps/console bundle); the rest of app-shell and the repo-wide lint/test shards belong to CI (round 1 ran all 313 metadata-admin + 65 studio-design test files green at 68eedfed1).",
      "mcp_calls": "0 — none",
      "api_writes": "1 — this os-dev-report comment through post-stamped (POST /repos/objectstack-ai/objectstack/dispatches relaying POST /repos/objectstack-ai/objectui/issues/10696/comments); its relay run and read-back are in the final message. git push (not REST) of 7e724988e3. No PR body PATCH (pr_body_replacement carries the body); the PR assignee write was not attempted, per the round-2 order. REST reads: GET issues/comments/5852290145.",
      "deviations": [
        "Design: chose to carry `regionKey` out of `locateFlowNode` and translate at the render site (the seat's preferred shape); `displayRegionLabel` was MOVED from previews/flow-region-view.tsx to a new leaf module previews/flow-region-label.ts rather than exported from the component .tsx, to reuse it without a cycle and without adding a react-refresh/only-export-components warning. previews/flow-region-view.tsx is therefore touched (its private function replaced by the import; behaviour unchanged, canvas tests green).",
        "The first tsc run in the recreated worktree reported ~1860 TS2307 module-resolution errors because `pnpm install` had produced no workspace dist/; the closure was built and tsc re-run (0 errors). The first reading is not a measurement and is not reported as one.",
        "Worktree /home/user/objectui-issue-10696 removed again after this round (node_modules deleted, `git worktree remove` on a clean tree), per the role file's cleanup step; a further round recreates it (install + closure build, about 5 minutes).",
        "H3 table column: file · enclosing symbol/prop instead of file:line (AGENTS.md #11), as in round 1."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: none measured (read from source; the pin harness is the lit instrument) · English under zh-CN in designer inspectors where NO catalogue row of the same value exists (this card forbids new keys): DashboardWidgetInspector WIDGET_TYPES labels and InspectorEmpty messages; the untitled-widget fallback `Widget N` (3 sites); ObjectFieldInspector LOOKUP_OPERATORS labels (partial rows engine.inspector.condition.op.* without the symbol prefix; none for at least / at most / contains / in / not in); FlowEdgeInspector branch suffix ` · default`; FlowNodeInspector aria-label `nested node location` and the `Must be a JSON object` error via setAdvError; VariableTextInput PICK_LABEL / SEARCH_LABEL / EMPTY_LABEL; flow-nested-selection.ts regionLabelOf `Body` (no same-concept row — the two Body rows are page-block body text); useFlowScope.ts GROUP_LABELS (Flow variables, Upstream outputs, Loop item, Trigger record) plus the three approval scope-group headings and the `pre-update row` detail; expression-validate.ts validateExpressionClient messages (Unbalanced parentheses/brackets, Expected a CEL expression…, Expected a text template…, the single-brace and map-literal explanations, Conditions are bare CEL) · dedupe words: designer inspector English literal no key zh-CN · widget type labels untranslated · lookup operator labels untranslated · VariableTextInput Insert a reference · useFlowScope group labels · carrier: the seat, as this family-closure card's remainder (needs keys; objectui#4662 carve-out decision)"
      ],
      "pr_body_replacement": "Fixes #10696\n\nClause-②: no\n\nA zh-CN author read `Search fields…` in the dashboard widget inspector's filter-binding field picker, while the designer catalogue already carried `engine.form.searchFields` in en and zh and `DatasetDefaultInspector` read it for the same combo. That site now reads the row. The census the triage asked for found seven more inspector sites passing an English literal where an `engine.*` row with the same English already existed; each now reads its row. No catalogue row is added or changed, so the objectui#4662 `engine.*` carve-out does not move, and en-US renders the same words as before.\n\nThis branch resumes the work a previous `os-dev` left at `68eedfed1` (release note `5851632349`); nothing it did was taken as verified. Round 2 (`7e724988e3`) adds the site the contract review of `68eedfed1` found missed: the flow node inspector's nested-node breadcrumb.\n\n## Premise check — H1, measured under zh-CN\n\nBase `383a34f52` (the branch point), pin from this branch: `DashboardWidgetInspector`'s filter-binding combo search box rendered the English literal under zh-CN — `Expected: \"搜索字段…\"`, `Received: \"Search fields…\"`. The lit control on the same base, `DatasetDefaultInspector`'s dimension-field combo, rendered `搜索字段…` (its case passed on base). H1 holds as the card stated it.\n\n## Census — H3, over `metadata-admin/inspectors/*.ts` and `*.tsx` (44 source files: the 27 components and the 17 helpers whose strings they render)\n\nMethod: an AST walk (TypeScript compiler API) over every JSX attribute value (direct, or inside a ternary / template / binary expression), every JSX text and expression child, and every object-literal text prop (`label`, `placeholder`, `title`, `aria-label`, `emptyText`, `message`, `help`, and the like), plus a broad pass over every string literal in those files so a word that flows into a label through a variable or a helper's return is seen too; class-name-shaped and key-shaped strings dropped. Each surviving literal was compared byte-for-byte against the EN values of `metadata-admin/i18n.ts`. Both passes ran on the base tree and on this head. Counts: the default pass keeps 289 of 1282 candidate literals on base and 283 of 1276 on head; the broad pass keeps 1989 of 3821 on base and 1982 of 3814 on head. The rows that disappear between base and head are the seven literals fixed at `68eedfed1`. The eighth site is different in kind: `flow-nested-selection.ts` `regionLabelOf` still returns the English `Try` / `Catch` / `Branch N` (it is the pure, i18n-free mirror of the canvas layout's own fallbacks, and its test pins that), so the census still lists those literals on head with their same-value rows — what changed is their reader, which now translates them at render. The first round's census walked `*.tsx` only and missed that helper; the review of `68eedfed1` found it. Sites are cited by file and enclosing symbol or prop, not by line, per AGENTS.md #11 (a pull-request body has no same file).\n\n### Fixed — a row with the same English existed; the site now reads it\n\n| file · site | literal before | row read now |\n|:--|:--|:--|\n| `DashboardWidgetInspector` · filter-binding `InspectorComboField` `searchPlaceholder` | `Search fields…` | `engine.form.searchFields` |\n| `ViewVariantInspector` · `ViewObjectPicker` `searchPlaceholder` (now a prop its one caller fills) | `Search objects…` | `engine.inspector.dataset.searchObjects` |\n| `ObjectFieldInspector` · `LookupConfigFields` dependsOn chip, remove button `aria-label` | `Remove NAME` | `engine.form.removeNamed` |\n| `ObjectFieldInspector` · `LookupConfigFields` filter row value `placeholder` | `value` | `engine.inspector.condition.valuePlaceholder` |\n| `ObjectFieldInspector` · `SummaryConfigFields` filter row value `placeholder` | `value` | `engine.inspector.condition.valuePlaceholder` |\n| `FlowEdgeInspector` · decision-branch picker, custom row `label` | `— Custom —` | `engine.inspector.flowEdge.branchCustom` (the approval-branch picker beside it already read it) |\n| `FlowEdgeInspector` · decision-branch picker, unnamed branch name (`nm`) | `Branch N` | `engine.flowRegion.branchN` |\n| `FlowNodeInspector` · nested-node breadcrumb, region crumb (label from `flow-nested-selection.ts` `regionLabelOf`) — round 2 | `Try`, `Catch`, `Branch N` | `engine.flowRegion.try`, `.catch`, `.branchN`, through `displayRegionLabel` (the canvas region header's own reader, `previews/flow-region-view.tsx`) |\n\n### Listed for the seat — English words with no row of the same value (⛔ no key added here)\n\n| file · site | literal | nearest row |\n|:--|:--|:--|\n| `DashboardWidgetInspector` · `WIDGET_TYPES[].label` (the widget-type select) | `KPI Metric`, `Bar Chart`, `Horizontal Bar`, `Line Chart`, `Area Chart`, `Pie Chart`, `Donut Chart`, `Funnel`, `Table`, `Pivot Table` | none (`engine.inspector.view.type.*` covers view types, not widget types) |\n| `DashboardWidgetInspector` · `InspectorEmpty` `message` | `Unsupported selection kind: KIND`, `The selected widget was removed from the draft.` | none |\n| `DashboardWidgetInspector` header, `DashboardDefaultInspector` add-widget title and widget list | `Widget N` (untitled-widget fallback, 3 sites) | none (`engine.flowRegion.branchN` is `Branch {n}`, another word) |\n| `ObjectFieldInspector` · `LOOKUP_OPERATORS[].label` | `= equals`, `≠ not equals`, the greater-than and less-than rows, `≥ at least`, `≤ at most`, `contains`, `in (any of)`, `not in` | partial: `engine.inspector.condition.op.equals` / `.notEquals` / `.greaterThan` / `.lessThan` carry the words without the symbol prefix; no row for at least, at most, contains, in, not in |\n| `FlowEdgeInspector` · decision-branch option `suffix` | ` · default` | none (`engine.inspector.flowEdge.typeDefault` is `Normal`; `engine.inspector.pageBlock.option.colorVariant.default` is `Default`, another meaning) |\n| `FlowNodeInspector` · nested-location `aria-label` | `nested node location` | none |\n| `FlowNodeInspector` · `commitAdvanced`, shown through `setAdvError` | `Must be a JSON object` | none |\n| `VariableTextInput` · `PICK_LABEL`, `SEARCH_LABEL`, `EMPTY_LABEL` | `Insert a reference`, `Search references…`, `No matching references.` | none |\n| `flow-nested-selection.ts` · `regionLabelOf` for the `body` region (the loop-body crumb) | `Body` | none of the same concept (the two `Body` rows, `engine.inspector.pageBlock.option.variant.body` and `engine.inspector.pageBlock.field.record:alert.body`, are page-block body text) |\n| `useFlowScope.ts` · `GROUP_LABELS` and the three approval scope-group headings (rendered as `VariableTextInput` `CommandGroup` headings) | `Flow variables`, `Upstream outputs`, `Loop item`, `Trigger record`; `Current record (live at node entry)`, `Trigger snapshot (at submit)`, `Flow variables`; the `pre-update row` detail | none |\n| `expression-validate.ts` · `validateExpressionClient` messages (rendered by `FlowExprIssue` and the edge condition) | `Unbalanced parentheses in …`, `Unbalanced brackets in …`, `Expected a CEL expression but got a … dialect.`, `Expected a text template but got a … expression.`, the single-brace and map-literal explanations, `Conditions are bare CEL (e.g. …).` | none |\n\n### Not words, or another channel — left literal, as PR objectui#10678 and PR objectui#10651 ruled for this family\n\n- Code samples and syntax hints in `placeholder`s: `record.amount * 0.2`, `record.status != 'draft'`, the arrow-function samples in the hook and action script boxes, `comma,separated,values`, the reference-syntax hint in `FlowObjectListField` (`current.FIELD · trigger.FIELD · vars.NODE.KEY`), `/api/v1/…`, `{ }`, `building` (an icon name), the CEL samples in `flow-node-config.ts`.\n- Sample values: `status`, `done` (its byte match with `engine.flowSim.status.done` is a coincidence — that row names a simulator status, this is a sample value in a field), `100`, `0`, `255`, `10`.\n- Stored values and type names rendered as code, or compared rather than shown: HTTP methods, `script`, currency codes (`USD ($)` …), the scope roots `previous` / `previous.FIELD`, `columns[N]`, the operator names in `datasetFilterCondition.ts` (`equals` is a stored operator, not a label), the type tokens `string` / `number` / `boolean` / `expression` / `true` the helpers compare against.\n- `flow-node-config.ts` field labels, help and option labels (`Trigger`, `Object`, `Record created`, `Cron schedule` …): translated at render through `localizeField` from `FLOW_FIELD_ZH` — a different channel, not this card's class.\n- `ViewVariantInspector`'s `TYPE_LABELS` map: read only after `t('engine.inspector.view.type.' + type)` echoes the key back, i.e. for a view type the catalogue lacks — a fallback behind the row, not a literal passed where a row exists.\n- `DashboardDefaultInspector`'s `New widget` / `New TYPE`: the default title written into the draft — author data, not chrome (the objectui#10678 boundary).\n\n## Round 2 — the nested-node breadcrumb (`7e724988e3`)\n\nThe review of `68eedfed1` (PR comment `5852290145`) found that `flow-nested-selection.ts` `regionLabelOf` returns `Try`, `Catch` and `Branch N`, `locateFlowNode` stores it as `regionLabel`, and `FlowNodeInspector` renders it in the `aria-label=\"nested node location\"` crumb, while `engine.flowRegion.try` / `.catch` / `.branchN` exist with the same English and the canvas region header already reads them — so a zh-CN author saw `尝试` on the canvas and `Try` in the inspector.\n\n**Shape chosen: carry the region key out of `locateFlowNode` and translate at the render site.** `NodeLocation` gains `regionKey` (the key `branch-N` carries the branch index), set beside the unchanged English `regionLabel`; `FlowNodeInspector` renders `displayRegionLabel({ key, label }, locale)` with the `locale` it already holds. The pure codec stays English and i18n-free (its own test pins that it mirrors the canvas layout's fallbacks), and the translation happens once, where the locale is. Threading a locale into the helper would have made every `locateFlowNode` caller locale-aware and duplicated the canvas's mapping in the codec.\n\n**Reused `displayRegionLabel` itself, no cycle.** It was a module-private function of `previews/flow-region-view.tsx`; it moved verbatim into `previews/flow-region-label.ts`, a leaf module whose only runtime import is `../i18n.js` (the `LabeledRegion` import is type-only). `FlowRegionView` and `FlowNodeInspector` both import it, so the mapping is stated once and the canvas header and the crumb read one word. A leaf `.ts` module rather than an `export` from the component `.tsx`, so no `react-refresh/only-export-components` warning is added; the parameter is `Pick` of `key` and `label`, so both callers fit. `Body` has no same-concept row and passes through, as does an authored branch name (the function's existing `Branch N` gate); en output is byte-identical (the en rows are the literals).\n\nRound-2 name-status against `68eedfed1`: M `.changeset/10696-inspector-literals-read-existing-keys.md` · M `inspectors/FlowNodeInspector.tsx` · M `inspectors/flow-nested-selection.test.ts` · M `inspectors/flow-nested-selection.ts` · M `inspectors/inspectorStrings.i18n-10696.test.tsx` · A `previews/flow-region-label.ts` · M `previews/flow-region-view.tsx`. Numstat: changeset +8 −2 · FlowNodeInspector +7 −1 · flow-nested-selection.test +2 −0 · flow-nested-selection +12 −0 · pin +105 −0 · flow-region-label +35 −0 · flow-region-view +5 −20; 7 files, +174 −23.\n\n## Pins — red on base, green on head, per-site ablation\n\n`inspectors/inspectorStrings.i18n-10696.test.tsx`, 22 cases: one zh case per fixed site reading the rendered attribute or text (the open combo's search box, a button's `aria-label`, an input's `placeholder`, the select trigger's text, the crumb's region segment) against the catalogue's zh row behind a guard (not the echoed key, not the en row); an en case per site; and a lit control on the same mount, already translated before this change (`DatasetDefaultInspector` for the card's own control; the nested-id hint for the breadcrumb). The breadcrumb block's outside control is the existing en crumb test in `FlowNodeInspector.test.tsx` (`opens a nested node with a container › region › node breadcrumb`), unchanged and green.\n\n- Head `7e724988e3`: the pin, `FlowNodeInspector.test.tsx`, `flow-nested-selection.test.ts`, `FlowCanvas.test.tsx`, `flow-canvas-layout.test.ts` and `flow-region-metrics.test.ts` together: `Test Files 6 passed (6)`, `Tests 99 passed (99)`, exit 0 (the pin's 22 among them). Round 1 head `68eedfed1`: `Tests 17 passed (17)`.\n- Base leg, round 2 — the six inspector-side sources (`DashboardWidgetInspector`, `FlowEdgeInspector`, `ObjectFieldInspector`, `ViewVariantInspector`, `FlowNodeInspector`, `flow-nested-selection.ts`) from `383a34f52`, pin from head: `Tests 10 failed | 12 passed (22)`, exit 1 — the ten failures are the seven zh cases of round 1 (`Received: \"Search fields…\"`, `\"Search objects…\"`, `\"Remove region\"`, `\"value\"` twice, `\"— Custom —\"`, `\"Branch 1 · record.amount > 100\"`) plus the three zh crumb cases (`Received: \"Try\"`, `\"Catch\"`, `\"Branch 2\"`); the en cases and the lit controls pass. Restore proven: each path's `git hash-object` equals its HEAD blob and `git diff HEAD` is 0 lines. Round 1's base leg (four sources) read `Tests 7 failed | 10 passed (17)`.\n- Per-site ablation, with objectstack's `scripts/ablation-replace.mjs` (one anchor declared to hit once; the write and the restore judged by blob hash on disk): each of the eight sites with its literal put back alone fails exactly its own zh case(s) and restores to the HEAD blob. Round 1, S1–S7: `1 failed | 16 passed (17)` each — S1 `searchFields`; S2 `searchObjects` (mutated inside `ViewObjectPicker`, so the passed prop is ignored); S3 `removeNamed`; S4 lookup `valuePlaceholder` (the summary case stays green); S5 summary `valuePlaceholder` (the lookup case stays green); S6 `branchCustom` (anchor lengthened to the decision-branch block, since the approval-branch picker carries the same row); S7 `branchN`. Round 2, S8 — the crumb back to `{loc?.regionLabel}`: `Tests 3 failed | 19 passed (22)`, exactly the three zh crumb cases (try, catch, unnamed branch); the en crumb case, the lit control and the other 17 stay green.\n- Two no-op attempts in round 1, recorded rather than hidden: the driver's first call ran the tool from outside the repository (refused before writing; hashes unchanged), and the first S2 reinstated the wrong text at the caller (`ReferenceError`, so zh and en fell over together — a broken-harness shape, not a reading). Both were redone; the readings above are from the redone legs.\n\n## Gates — at `7e724988e3`; heavy runs through `os-verify-lock`\n\n| gate | exit | reading |\n|:--|:--|:--|\n| `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' run build` (the closure, so `tsc` reads real `dist/*.d.ts`) | 0 | |\n| `tsc --noEmit` and `tsc -p tsconfig.test.json` in `packages/app-shell`, run directly | 0, 0 | 0 errors; `--listFiles` names the pin once in the test project |\n| `pnpm exec vitest run` over the six files above | 0 | `Test Files 6 passed (6)`, `Tests 99 passed (99)` |\n| round 1, at `68eedfed1`: `metadata-admin/inspectors/` (103 files), the rest of `metadata-admin/` in two halves (105 + 105 = all 313 tracked test files), `views/studio-design/` (65 files) | 0 each | `1428 passed | 1 skipped`, `881 passed`, `1287 passed`, `359 passed` |\n| eslint, plain form, on the 6 TS files round 2 touches | 0 | `--format json`: 6 files, 0 errors, 3 warnings — `FlowNodeInspector.tsx`'s pre-existing `no-explicit-any` and `react-hooks/set-state-in-effect`, equal to its blob at `68eedfed1` (`--stdin`); the new module, the codec, its test and the pin have 0. Round 1's 5 files: 0 errors, 26 warnings, per file equal to base |\n| `check-changeset-presence` | 0 | 10 source files of 1 released package, 1 changeset |\n| `check:changeset-claims` (base `383a34f52`, head) | 0 | no pending changeset names a file this change touches |\n| `changeset:check` (fixed group, no major) | 0 | |\n| `check:new-line-citations` | 0 | 0 new citations |\n| `check:control-bytes` | 0 | OK |\n| `check:i18n-keys`, `check:i18n-drift`, `check:i18n-dead-keys` | 0 each | drift: 0 en values changed, 0 keys added or removed in the designer table |\n| `check:vi-mock-specifiers`, `check:vi-mock-inherit`, `check:vi-mock-override-shape`, `check:test-path-roots` | 0 each | the pin's two new `vi.mock` calls resolve |\n| round 1 only: `check:i18n-designer-parity`, `check:pending-changeset-literals` | 0 each | |\n\n- The eslint narrowing is a measurement: the population is the changed `.ts`/`.tsx` files, which the config's `**/*.{ts,tsx}` block covers; the JSON output counts the files; `eslint.config.js` configures no type-aware linting (a grep for `projectService` or a `parserOptions` project exits 1), so this diff cannot move an untouched file's verdict.\n- The test narrowing: the inspectors are reached only through `inspectors/index.ts` and the inspector registry; `displayRegionLabel`'s two importers are `flow-region-view.tsx` (whose canvas tests ran) and `FlowNodeInspector.tsx`; the package's `exports` do not change. The rest of `packages/app-shell` and the repo-wide `pnpm lint` / `pnpm test` shards belong to CI.\n- NOT MEASURED: `check:eager-locale-catalogues` and the eager-closure budget, which read a built `apps/console` bundle (`performance-budget.yml` runs both).\n\n## Published prose — H5\n\nPending changesets that name these strings: `10586-inspector-strings-i18n.md` (PR 1 of the family: \"plus the existing `engine.form.add` and `engine.form.searchFields`… The dataset inspector reads the `locale` its host already passes\") and `10586-inspector-strings-i18n-2.md` (PR 2: the combo's defaults, \"A label the caller passes still wins\"). Neither sentence is falsified: this change adds callers that pass a translated label, which is exactly the case both describe. No dated note is needed and no frontmatter moved. This PR's own changeset now titles eight words and carries a bullet for the breadcrumb naming the canvas header as the rows' existing reader.\n\n## Serial\n\n`origin/main` `1fc77fd806` (fetched into a private per-worktree ref just before this round's push) touches none of this branch's files since the base `383a34f52`, and `git merge-tree --write-tree` against it is clean (exit 0). Not merged.\n\n## Acceptance notes\n\n- The seat's list above (widget-type labels, `InspectorEmpty` messages, `Widget N`, the lookup operator labels, ` · default`, the nested-location `aria-label`, `Must be a JSON object`, `VariableTextInput`'s three labels, the `Body` region crumb, `useFlowScope`'s group headings, `expression-validate`'s client messages) needs new rows, which this card forbids; it is the family's remaining English under zh, read from source with the pin's harness as the instrument.\n- `ViewVariantInspector`'s `ViewObjectPicker` takes `searchPlaceholder` as a required prop rather than reading `useMetadataLocale()` itself, because its host already carries `locale` — the `locale` prop channel PR objectui#10678 named for hosted inspectors.\n- `flow-region-view.tsx` loses its private `displayRegionLabel` and imports the moved one; its behaviour is unchanged and `FlowCanvas.test.tsx`, `flow-canvas-layout.test.ts` and `flow-region-metrics.test.ts` ran green.\n\n---\n_Generated by [Claude Code](https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN)_\n"
    }

    Generated by Claude Code

  8. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10741 at 7e72498; body, ready and auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the report's own account. Claim 5851771741 (a re-claim; the first was 5838948159). It executes triage 5838471955 (bug · p3, the family-closure card).

    Implemented-by:  claude/issue-10696-widget-search-placeholder
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    the change eight designer-inspector sites that passed an English literal now read the engine.* row carrying the same English, so a zh-CN author sees Chinese there. The sites: Search fields…, Search objects…, Remove NAME, value ×2, — Custom —, Branch N, and, in round 2, the nested-node breadcrumb's Try / Catch / Branch N. The breadcrumb reads the canvas header's own displayRegionLabel, moved to the leaf module previews/flow-region-label.ts, so the canvas and the inspector show one word. No catalogue row is added or changed, and en output is byte-identical
    surface the move touches previews/flow-region-view.tsx and adds previews/flow-region-label.ts, outside inspectors/. The review judged it minimal. Exporting in place would trip react-refresh/only-export-components (measured) and pull the canvas component graph into the inspector. The seat accepts it within the ruling to reuse displayRegionLabel
    pins 22 cases: one zh case per site against the catalogue row, en cases, and lit controls. Base: 10 red of 22. Head: 22 green. There is a per-site ablation for all eight sites, each proven on disk, plus the reviewer's canvas-local-copy leg
    Clause-②: no / semver '@object-ui/app-shell': patch; exports untouched; the objectui#4662 engine.* carve-out does not move
    reviews two rounds, both at CONTRACT_REVIEW_TIER. Round 1 FAIL at 68eedfe (5852290145): one missed site, the breadcrumb, found because the census had walked *.tsx only. Round 2 PASS at 7e72498, posted with this landing. The seat read both transcripts: every model turn ran at tier
    seat edits to the dev's body four, each from the PASS record: the Fixed row 8 parenthetical names flow-region-label.ts; "moved verbatim" becomes "moved, with its body unchanged (the signature gains export …)"; the Serial line notes the re-read at ee6f6c6a45; the control test's full title
    serial git merge-tree --write-tree against main ee6f6c6a45 is clean; no open PR touches the 11 files
    governed NOT GOVERNED (check-governed-queue-guard --test, 11 real paths)
    CI head 7e72498: 39 green, 3 skipped, 1 red. The red check is Inert vi.mock Specifier Check, main-red (objectui#10731, this seat's p1, in progress)
    assignee none: the dev's label-write --assign on the PR was refused by the permission classifier, and the seat has surfaced it to the maintainer rather than re-issue it
    closing keywords Fixes #10696 only

    Findings, each with its disposition

    • The family's remaining English under zh-CN, where no row of the same value exists (widget-type labels, InspectorEmpty messages, Widget N, the lookup operator labels, · default, the nested-location aria-label, Must be a JSON object, VariableTextInput's three labels, the loop Body crumb, useFlowScope's group headings, expression-validate's client messages) ⇒ one card from this seat. Closing it needs new catalogue rows, so it is a decision on the objectui#4662 carve-out.

    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:i18nThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions