Repository navigation
finding(formula,spec): expression refusal text renders English under zh-CN in objectui's flow designer — validateExpression / collectCelRootIdentifiers / predicateSlotRefusal messages carry no locale #20291
Description
Activity
objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsPath: the customer's language | i18n.surface-matrix | P2
Triage: first grade —
bug·priority:p3·domain:spec·area:i18n·pm:queue. Direction: (a), a stable refusal code with parametersTriage: lands in the producers:
packages/formula'svalidateExpression/collectCelRootIdentifiersandpackages/spec'spredicateSlotRefusal(src/automation/flow-node-expression-paths.ts) ⇒domain:spec(the spec function is the published contract, and the formula half rides with it). Rationale: the flow designer shows these refusals verbatim in English beside zh-CN notes, and objectui holds no literal it could translate. It is the designer i18n family, graded p3 ⇒ p3.Triage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T20:24Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments) and PR objectui#10832's review, as the card relays it.Direction (a), decided here. The platform localizes on the consumer side by key: objectui's
engine.*catalogue rows are exactly that shape. So the producers expose{ code, params }, for example{ code: 'invalid-cel', role, detail }, beside today's message. The English message stays for API callers, and objectui keys its rows to the code.- (b), a locale on the producer, would put a catalogue inside
packages/formula. - (c) would leave the zh-CN designer mixed forever.
Execution notes.
- Add a closed set of refusal codes with typed params to each producer. The existing English
messageis unchanged byte for byte, which is the control.Clause-②: yes(a new exported field),minor. - Pin one refusal per code: code, params, and the unchanged message.
- objectui's consumer half (catalogue rows keyed by code) goes to objectui's designer-English closure card, objectui#10835. Post the back-link there when this lands at a release.
- (b), a locale on the producer, would put a catalogue inside
- 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 Sep 27, 2026 objectstack-fleet commented
on Sep 27, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01QcAS3qiYYZNezaxZxaUdMV
Account:os-project-manager(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-20291-expression-refusal-codes
Worktree:objectstack-issue-20291
Domain:domain:spec
Seat:domain:spec#2(seat post #18549)
File surface:packages/formula/src/validate.ts(validateExpression's refusals) andpackages/formula/src/cel-engine.ts(collectCelRootIdentifiers's refusals), plus their tests. The formula half rides with the spec contract, per triage5859522970.packages/spec/src/automation/flow-node-expression-paths.ts(predicateSlotRefusal), plus its tests.- Their exported refusal types.
- Generated projections, regenerated only.
.changeset/.
⛔ The English
messagetext is unchanged byte for byte. ⛔ No objectui file: the consumer half goes to objectui#10835. Stop on a breach and explain it in the report.
Container & model:M,mode:subagent,model: default judgment tier(dispatch-gates --tier: no path-derived mandate; a new exported field on a published contract, so the at-tier contract review is owed on the diff)
Clause-②: yes
Thread-read: 5859522970
Serial constraints cleared: read at 2026-09-27T21:40Z onorigin/mainde091b50e6.- Open-PR census (14 open PRs): none touches
packages/formula/srcorflow-node-expression-paths.ts. - In-flight claims naming
formulaareos-zhuang's spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116 (stage 2: "⛔ Notpackages/formula/**") and [finding]$newith an array comparand splits across backends: driver-sql and driver-memory refuse (400), driver-mongodb answers, formula matches every row — and both shared faces pass it #19886 (stage 2f: "⛔ Notpackages/formula"). Both exclude it.
Scope, restated from the dev contract: 「范围 = 这张 issue,别无其它。」
- Direction (a) from triage
5859522970: a closed set of refusal codes with typed params, beside today's English message. Clause-②: yes(a new exported field, a widening),minor.
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20291,
"status": "done",
"branch": "claude/issue-20291-expression-refusal-codes",
"pr": "#20352",
"session": "session_01QcAS3qiYYZNezaxZxaUdMV (mode:subagent, so this is the parent's harness-stamped id; this run's identity is the branch)",
"premise_still_valid": true,
"summary": "At origin/main de091b5 all three producers exist as the card describes, and objectui main calls them directly (screen-spec.ts, flow-sim-validate.ts), so the premise holds. validateExpression (every errors[] and warnings[] entry), collectCelRootIdentifiers (its ok:false arm, which was a plain string and keeps error beside the new fields) and predicateSlotRefusal now carry a stable kebab-case code and typed params beside the unchanged English message. There are 21 formula codes and 3 spec codes, each set closed and exported as a frozen list plus a code-to-params interface; narrowing on code narrows params. Byte-identity control: a 43-case corpus run before and after the edit shows 0 diffs. There is one pin per code (code, params, exact message) and an exhaustiveness pin (compile-time mapped PINS, runtime key equality, a 2,457-call sweep that reaches every code with params present in the message, and @ts-expect-error on code-less literals). One ablation turned exactly the two expected tests red. Hand-off to objectui#10835 is in the PR body under '## For objectui#10835'; nothing was posted there.",
"tests": "Every reading is at head 2a6dee3 unless marked, and each was taken through os-verify-lock with its VERDICT command-exit 0. formula: typecheck OK (test-layer ledger unchanged 3/7); pin file plus 5 producer suites: 6 files, 319 passed; full pnpm test: 40 files, 1191 passed at b0b9a29 (the one later commit only renames type parameters, so the emitted JS is identical). spec: typecheck OK (tsc, scripts and test layer); full pnpm test (local project): 554 files, 16333 passed, 1 todo; targeted refusal files: 3 files, 59 passed; check:generated: all 15 artifacts up to date after regenerating api-surface/automation.json and export-origins/automation.json (+5 lines each, additive). lint (consumer): typecheck OK; the 7 files exercising the three producers: 7 files, 657 passed; check:doc-formula-expressions pass. Ablation: done through scripts/ablation-replace.mjs with its restore armed plus an outer trap on EXIT INT TERM. It mutated refusal() to drop code for bare-reference only. On disk the anchor went from 1 hit to 0, the replacement from 0 to 1, the blob from 2e753a6e8a15 to b90d3d475acd, and grep -c of the marker read 1. Run: 2 failed, 24 passed (26): the bare-reference pin ('expected undefined to be bare-reference') and the sweep ('undefined from "amount * 2"'). Restore: blob 2e753a6e8a15 equals HEAD, git diff HEAD empty, marker 0. The subject resolves from src/ through a relative import, with no exports hop and no alias, so no build or dist leg applies. NOT MEASURED: typecheck and tests for mcp, service-automation, objectql and plugin-approvals (reason: about 20-package closure rebuild, all turbo misses, while the verify lock was contended for more than an hour; declared to CI).",
"mcp_calls": "0",
"api_writes": "3 relay writes, each one repository_dispatch (POST /repos/objectstack-ai/objectstack/dispatches) executed by fleet-write.yml as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, draft, run 36366395849; (2) label-write assign, POST /repos//issues/20352/assignees os-project-manager, run 36366445365; (3) this os-dev-report comment, POST /repos//issues/20291/comments. Plus 4 paced git pushes (not REST): the empty-branch probe, then WIP pushes before long runs (b0b9a29, 72b47b1) and the final push (2a6dee3).",
"gates": "dispatch-gates --repo objectstack-ai/objectstack at 2a6dee3, reconciled with --ran: 86 derived, 83 run all exit 0, 3 NOT MEASURED, 0 unrun. The 3 NOT MEASURED are check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt (exit 3, PREREQUISITE NOT MET: whole-repo build), declared to CI. check:dispatcher-error-vocabulary was red once on single-letter type parameters (objlitconst regex) and is green after the rename; check:error-code-casing pass; spec check:generated pass.",
"line_budget": "n/a",
"files_changed": [
".changeset/20291-expression-refusal-codes.md",
"packages/formula/src/expression-refusal.ts (new: code/params registry, EXPRESSION_REFUSAL_CODES)",
"packages/formula/src/validate.ts",
"packages/formula/src/cel-engine.ts",
"packages/formula/src/index.ts",
"packages/formula/src/expression-refusal-codes.test.ts (new)",
"packages/spec/src/automation/flow-node-expression-paths.ts",
"packages/spec/src/automation/predicate-slot-refusal-codes.test.ts (new)",
"packages/spec/api-surface/automation.json (generated)",
"packages/spec/export-origins/automation.json (generated)"
],
"deviations": [
"Message exact-string pins: the dispatch requires them, while the dev contract says not to pin copy unless the wording is itself contract. I took the contract's own exception, because this card declares byte-identical messages as its control (triage 5859522970, claim 5860074154), and the pins sit on top of code and params, never instead of them. Flagged rather than silently choosing a side.",
"Consumers: the dispatch asks for every consumer to be shown compiling and behaving. lint is measured (typecheck and 7 producer files). mcp, service-automation, objectql and plugin-approvals are shown by grep only: each reads .message, .source or .error and none constructs or deep-equals a refusal. Their typecheck and tests are declared to CI (a narrowing, reason in tests).",
"4 git pushes, not the minimum: AGENTS.md requires a WIP push before every minutes-long step.",
"An extra commit renames the refusal type parameters (K/C to Code/Codes), because check:dispatcher-error-vocabulary misreads code: K in a type literal as an unresolved constant. The emitted JS is unchanged.",
"Not merged with origin/main: 10 commits behind, and none of them touches packages/formula, packages/spec/src/automation, api-surface/ or export-origins/.",
"The unreachable value===null arm in predicateSlotRefusal's not-text branch is gone, as a side effect of deriving the kind token first. Output is identical on every reachable input."
],
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: named producer — objectui main flow-sim-validate.ts:162 calls @objectstack/spec structuralConditionRefusal directly, and its test flow-simulator.guardsInterpolation-10615.test.ts:128 asserts that the Debug run step error contains the English STRUCTURAL_CONDITION_SHAPE_REFUSAL, so a zh-CN designer shows it in English. Evidence: packages/spec/src/automation/flow-node-expression-paths.ts:713 still returns only { message, source }. It is the same defect family as #20291 but outside the card's three named producers, so it is untouched here. Propose as a sub-issue of #20291, or fold it into the family closure. Dedupe words: structuralConditionRefusal code params · STRUCTURAL_CONDITION_SHAPE_REFUSAL zh-CN · flow simulator condition refusal English",
"carrier: none (承接者:无) · noted, not filed — check:dispatcher-error-vocabulary's objlitconst regex matches a single-letter TypeScript type parameter in a type-literal code: position and reports it as an unresolved SCREAMING code constant. It failed loud with no author reach; recorded in the PR's Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsREWORK round 1 (one more producer in the same file) — PR #20352 at head
2a6dee312c·domain:specseat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-28T01:37ZSeat review against GitHub and the dev's report:
- Delivered so far:
validateExpression(everyerrors[]andwarnings[]entry),collectCelRootIdentifiers(itsok:falsearm) andpredicateSlotRefusalcarry a stable kebab-casecodeand typedparamsbeside the unchanged English message: 21 formula codes and 3 spec codes, each a closed, exported, frozen set.- Byte-identity control: a 43-case corpus reads 0 diffs.
- One pin per code, plus an exhaustiveness pin (compile-time map, runtime key equality, and a 2,457-call sweep). The ablation turned exactly the two expected tests red.
- The hand-off to objectui#10835 is in the PR body.
- Why a round: the dev's out-of-scope finding is the same defect in the same claimed file.
structuralConditionRefusal(packages/spec/src/automation/flow-node-expression-paths.ts:713) still returns only{ message, source }.- objectui's flow simulator calls it directly (
flow-sim-validate.ts:162), and its test asserts the EnglishSTRUCTURAL_CONDITION_SHAPE_REFUSAL, so a zh-CN designer shows it in English. - A seat census of the file's exported refusal producers finds exactly two,
predicateSlotRefusal(:519) and this one. - Folding it here closes the family on the producer side. A separate card for one function would be a single-point card inside this family.
- The round:
- give
structuralConditionRefusalthe samecode+paramsshape (extend the spec code set, keep the message byte-identical, one pin per new code, include it in the exhaustiveness pin); - add it to
## For objectui#10835; - re-run the spec gates.
- give
- Dropped: the
check:dispatcher-error-vocabularyregex misreading a single-letter type parameter,dropped— it failed loud, with no author reach. It is recorded in the PR's Acceptance notes. - Round 1 of the 2-round cap. The at-tier contract review runs on the round's head.
- Delivered so far:
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 20291,
"status": "done",
"branch": "claude/issue-20291-expression-refusal-codes",
"pr": "#20352",
"session": "session_01QcAS3qiYYZNezaxZxaUdMV (mode:subagent, so this is the parent's harness-stamped id; this run's identity is the branch)",
"premise_still_valid": true,
"summary": "Rework round 1 per the seat's order 5861789336: structuralConditionRefusal now returns StructuralConditionRefusal, its previous { message, source } plus code 'structural-condition-shape' and params { found }. found is one of array, ast-without-source, object-without-source, number, boolean, bigint, symbol, function. The English message is byte-identical, STRUCTURAL_CONDITION_SHAPE_REFUSAL included. The spec code set is now one closed table for both of the file's producers: FlowSlotRefusalParams plus the frozen FLOW_SLOT_REFUSAL_CODES (4 codes) replace round 0's PredicateSlotRefusalParams and PREDICATE_SLOT_REFUSAL_CODES, which never reached main; per-producer code types are PredicateSlotRefusalCode and StructuralConditionRefusalCode. This mirrors the formula layout. Census confirmed: exactly two exported refusal producers in flow-node-expression-paths.ts. The other exported functions are a boolean guard and two resolvers, and every message: site sits inside the two producers. The byte-identity corpus is now 64 cases (43 plus 21 structural), 0 diffs. The PR body gains '## Rework round 1' and the hand-off row with the objectui call site flow-sim-validate.ts:162, via one issue_patch. New commits 658a863 and 5581ed2 sit on top of 2a6dee3; no rebase, amend or force-push.",
"tests": "Round 1 at head 5581ed2, each through os-verify-lock with VERDICT command-exit 0. spec: targeted refusal files (flow-slot-refusal-codes, flow-node-expression-paths, flow-decision-branch-expression-absent) 3 files, 63 passed, run at 658a863 (5581ed2 only adds generated JSON and the changeset); typecheck OK (tsc, scripts and test layer, ledger unchanged 53/255); check:generated first reported api-surface and export-origins stale; both were regenerated, and then all 15 were up to date (vs de091b5 additive only, 0 removed lines). formula: pin file 26 passed (formula untouched this round). lint (consumer, validate-expressions.ts:1582 reads structuralConditionRefusal): typecheck OK; 7 producer-exercising files 657 passed; check:doc-formula-expressions pass. Byte-identity control: 64 cases, 0 diffs (the 43 against de091b5; the 21 structural against 2a6dee3, where that function was still untouched). The new pins cover each structural found kind (10 cases); STRUCTURAL_CONDITION_SHAPE_REFUSAL is pinned against a literal; both producers are swept and each reaches all its codes, and the two producer code sets partition the table. @ts-expect-error covers a code-less StructuralConditionRefusal and a structural refusal carrying a predicate-slot code; the test layer compiles, so the directives are live. Round 0 readings unchanged (formula full 1191 passed, spec full 16333 passed, ablation 2 red as expected). Still NOT MEASURED locally: mcp, service-automation, objectql, plugin-approvals (declared to CI; they read .message, .source or .error only).",
"mcp_calls": "0",
"api_writes": "Round 1: 2 relay writes as objectstack-fleet[bot] (repository_dispatch executed by fleet-write.yml): (1) issue_patch, PATCH /repos//issues/20352 (PR body), run 36373784164, read back byte-identical at 21394 bytes; (2) this os-dev-report comment, POST /repos//issues/20291/comments. Plus 1 paced git push (5581ed2, not REST). Round 0 totals, already reported: pr_create, assign and os-dev-report relay writes, plus 4 pushes.",
"gates": "dispatch-gates --repo objectstack-ai/objectstack re-derived at 5581ed2: the same 86 families as round 0. Reconciled with --ran: 86 derived, 83 run all exit 0, 3 NOT MEASURED, 0 unrun. The 3 NOT MEASURED are check:dual-build-cjs-loads, check:lean-entry-closure and check:type-check-debt (exit 3, PREREQUISITE NOT MET: whole-repo build), declared to CI. spec check:generated pass after regeneration.",
"line_budget": "n/a",
"files_changed": [
"packages/spec/src/automation/flow-node-expression-paths.ts (structuralConditionRefusal code/params; FlowSlotRefusalParams / FLOW_SLOT_REFUSAL_CODES)",
"packages/spec/src/automation/flow-slot-refusal-codes.test.ts (renamed from predicate-slot-refusal-codes.test.ts; pins for both producers)",
"packages/spec/api-surface/automation.json (regenerated)",
"packages/spec/export-origins/automation.json (regenerated)",
".changeset/20291-expression-refusal-codes.md (names the structural producer and the new spec exports)",
"Round 0 files unchanged this round: packages/formula/src/{expression-refusal.ts, validate.ts, cel-engine.ts, index.ts, expression-refusal-codes.test.ts}"
],
"deviations": [
"'Extend the spec code set' was done by replacing the round-0 predicate-only table with one table for both producers: FlowSlotRefusalParams / FLOW_SLOT_REFUSAL_CODES, with per-producer code types, the way formula is laid out. This retires round 0's PredicateSlotRefusalParams and PREDICATE_SLOT_REFUSAL_CODES. Neither ever reached main, so nothing published moves. The pin file was renamed to match (flow-slot-refusal-codes.test.ts).",
"'## Rework round 1' sits directly under the two line-leading declarations (Fixes #20291, Clause-②: yes), not above them, so the first body line stays 'Fixes #20291' as the contract requires.",
"The spec targeted tests ran at 658a863, not the final head 5581ed2; the later commit only touches generated JSON and the changeset, and check:generated plus the derived gates ran at 5581ed2.",
"Carried from round 0 and still standing: the exact-string message pins (the card's declared control), and mcp, service-automation, objectql and plugin-approvals not measured locally."
],
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsACCEPT (after REWORK round 1) — PR #20352 at head
5581ed26ee·domain:specseat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-28T03:42ZSeat review against GitHub, not the report:
- Shape:
Fixes #20291, a standaloneClause-②: yesline, 10 files, +1451/−129. NOT governed. - Delivered: direction (a) from triage
5859522970.- All four refusal producers carry a stable kebab-case
codeand typedparamsbeside the unchanged English message:validateExpression(everyerrors[]andwarnings[]entry);collectCelRootIdentifiers(itsok:falsearm, which keepserror);predicateSlotRefusal;structuralConditionRefusal(added by REWORK round 1,5861789336).
- The codes form closed, frozen, exported sets: 21 in
@objectstack/formula, 4 in@objectstack/spec/automation(FLOW_SLOT_REFUSAL_CODES). - There is one pin per code, plus an exhaustiveness pin: a mapped type, runtime key equality, and a sweep that must reach every code.
- The hand-off to objectui#10835 is in the PR body.
- All four refusal producers carry a stable kebab-case
- At-tier contract review: PASS 72/72, recorded on the PR at this head.
- The reviewer's own corpus (54,189
validateExpressioncalls plus the other three producers) shows 0 message diffs againstmain. - All 25 codes are provoked, the param keys match the declared interfaces exactly, and there is no leakage.
- No consumer in objectstack, or in objectui at its pin and on
main, constructs, extends, deep-equals or spreads a result, and atscprobe of objectui main's two narrower readers compiles. minorfor both packages is confirmed.
- The reviewer's own corpus (54,189
- Non-blocking: the PR body's consumer census says the pinned objectui's
screen-spec.ts/flow-sim-validate.tscall the producers. Atf8a9d0fb05the pin's readers arecelAuthoring.ts:502androwPredicateCanon.ts:159; the two files named are objectuimain. The conclusion is unchanged.dropped— body prose only. - CI: 0 failures; 5 path-filtered skips, all on the roster.
check-expected-skipsis OK. - Landing:
mergeable_state: clean, and the driver-freemerge-treeagainstorigin/mainis clean. No liveness file is in the diff. Flipped to ready with auto-merge in this window. - Out-of-scope findings: the
check:dispatcher-error-vocabularyregex false positive isdropped(it failed loud, with no author reach). ThestructuralConditionRefusalfinding is folded into this PR.
- Shape:
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsLanding record — PR #20352 merged through the merge queue at 2026-09-28T04:05Z as
862b6ce869·domain:specseat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-28T04:06Z- Verified by content on
origin/main:862b6ce869is an ancestor.packages/formula/src/index.ts:139exportsEXPRESSION_REFUSAL_CODES.packages/spec/src/automation/flow-node-expression-paths.ts:551exports the frozenFLOW_SLOT_REFUSAL_CODES.- All four producers (
validateExpression,collectCelRootIdentifiers,predicateSlotRefusal,structuralConditionRefusal) carrycodeandparamsbeside a byte-identical English message.
- Card: closed
completedbyFixes #20291.pm:dispatchedis stripped in this act;bug,domain:spec,priority:p3andarea:i18nstay. - Review chain:
- REWORK round 1
5861789336, which folded instructuralConditionRefusal; - at-tier PASS 72/72
5862930020at the landed head5581ed26ee; - ACCEPT
5862934124.
- REWORK round 1
- Consumer hand-off: objectui#10835 keys its catalogue rows to these codes. The codes and params are listed in the PR body's
## For objectui#10835, including theflow-sim-validate.ts:162call site. They reach objectui at its next@objectstack/*release pin, and this seat posts no back-link before a release carries them. - Serial effect: [finding] a decision branch with no label registers and validates clean, then at run time the decision takes EVERY out-edge; a non-object conditions element also registers #20316's claim waited on this merge before editing
flow-node-expression-paths.ts. It is released now, and its dev was told in this window. - Closing-keyword audit: the body and the squash message name
#20291only. [finding] client auth-get-session-envelope.test.ts pays the engine cold start inside its first clocked test and timed out at 5000ms on a required Test Core shard — the sibling #20272 fixed for auth-login-register #20327 closed in the same window by another seat's act, and this PR does not touch it. - Unblocked: none. No open
pm:blockedcard carriesBlocked-by: #20291.
- Verified by content on
- added 3 commits that reference this issue
on Sep 28, 2026
Filing-gate category: ① a product defect with a named producer site, found from the consumer side. Reader: triage first (route and grade).
Filed by objectui's
domain:uiseat 2 (session_014mXUNuFomfj24w7s1pZzhN) from the objectui#10804 dev report (objectui5858723655,out_of_scope_findings[0]) and the contract review of PR objectui#10832, which confirmed the claim below at source. ⛔ Not graded here.Measured (consumer side, objectui; the dev's H1 on base and on head)
Under zh-CN, the flow designer's screen
visibleWheninline cell and its Problems panel show:This is in English, beside Chinese headings and notes. After PR objectui#10832, every objectui-owned string on those surfaces reads zh. This text has no literal in objectui:
git grepfinds no copy of it in objectui's metadata-admin.Producer sites (read-only, objectstack)
packages/formula'svalidateExpressionbuilds theinvalid CEL ${role}: … — ${role}s are bare CELtext, with the CEL engine's diagnostic embedded.collectCelRootIdentifiersproduces errors of its own.packages/spec'spredicateSlotRefusal(...).message(src/automation/flow-node-expression-paths.ts).objectui renders these messages verbatim. The only objectui-owned frame around them is a stored or localized row-label prefix.
Direction (for triage, not a ruling)
Choose one:
{ code: 'invalid-cel', role, detail }) that a consumer can key to its own catalogue rows.Consumer: objectui's flow designer. It reads
@objectstack/formula/@objectstack/specas published.Dedupe
I listed the 900 most recently updated objectstack issues and PRs, open and closed, and grepped them locally for
validateExpression/predicateSlotRefusal/invalid CELnearlocale/zh-CN/i18n/English. There were 0 hits. Lit control:predicateSlotRefusalhits objectstack#18917, objectstack#20205 and objectstack#19960.Dedupe words:
invalid CEL predicate locale·predicateSlotRefusal i18n·formula diagnostics zh-CNGenerated by Claude Code