Skip to content

docs(skills): objectui teaches CEL for bare-string predicates - #27889

Merged
os-zhuang merged 4 commits into
mainfrom
claude/project-thread-7yo6br
Oct 30, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/project-thread-7yo6br

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Elon · project thread

domain:skills#2 · os-elon-musk · session_016WV4d1Ffw7D64ARzogMukj

Fixes #27587

维护者速读

  • 改了什么:skills/objectui 里的裸字符串谓词示例全部改成 CEL 写法(==、!=、&&、!),运算符清单注明只适用于 ${…} 模板;指南新增一张 JS → CEL 对照短表;eval 2 改为给出 JS 写法、要求改写成 CEL。
  • 为什么改:fix(ui-core): read a bare-string predicate as CEL shorthand, as the spec and server do #27584 合入后,控制台把裸字符串谓词按 CEL 读(和规范、服务端一致),技能还在教 === 这类 JS 写法,照抄的 AI 写出的 hidden / visibleWhen 会出错并把节点藏起来。
  • 风险与回滚:只改文档与 eval,无代码、无 schema 变化;回滚即 revert 本 PR。三个文件都在令牌天花板内,没有抬任何一行。
  • 先看:skills/objectui/guides/schema-expressions.md 的 "Condition fields" 一节(新增的 FROM → TO 清单)。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs 全绿;pnpm --dir ui exec vitest run scripts/__tests__/check-skill-eval-tokens.test.ts 29/29 通过。
  • 需要你做的:本 PR 欠一次契约复审(fable 档,席位负责),落地为 Tier H,需治理审批人批准。

What changes

  • rules/protocol.md: the evaluated-fields table shows "hidden": "data.status == 'draft'" (was ===), and the visibleWhen row says a bare string is CEL.
  • guides/schema-expressions.md:
    • The architecture overview says a bare condition string is CEL, evaluated by @objectstack/formula as on the server; the recursive-descent parser is named for ${…} templates only.
    • "Supported operators (full list)" now says it is the ${…} grammar only.
    • "Condition fields": the bare example is data.status != 'active', and a FROM → TO list follows: === / !== → == / !=; list.includes(x) → x in list, s.includes('x') → s.contains('x'); list.length → size(list); a data.* key the scope may lack (data.x == 1, !data.x, ?., ??) → has(data.x) && data.x == 1.
    • The row-predicate bullet says only a ${…} condition still reads on the legacy engine; a bare === / .includes() faults on CEL.
    • The field-level section no longer says node-level conditions run on SafeExpressionParser; troubleshooting item 4 says "a bare CEL string or a ${} template".
    • Paid down in the same file: the bindable-text-keys paragraph under "What gets expression-evaluated" restated troubleshooting item 1 word for word and is cut.
  • evals/schema-expressions.json: eval 2 now hands over "visibleWhen": "data.role === 'admin' && data.status === 'active'" and expects the CEL rewrite (must_contain CEL and status == 'active', a token the guide teaches).
  • .changeset/27587-objectui-skill-teaches-cel-predicates.md: @objectstack/skills patch.

Why

PR #27584 (#27457) made the console read a bare-string predicate as CEL, the way @objectstack/spec defines it and the server reads it. Its changeset carries not-required (no-migration-prescription), so the skill is where the JavaScript spellings get redirected. Until now the skill taught those spellings, and an agent following it wrote a gate that faulted and hid the node.

Verification

Each FROM → TO row was measured on the console's ExpressionEvaluator.evaluateCondition (built @objectstack/formula, throwOnError and onFault), on this branch's base:

Spelling Console reading
data.status === 'draft' parse fault, Unexpected character: =
data.status == 'draft' true
data.tags.includes('vip') / data.name.includes('urgent') runtime fault, no matching overload for 'dyn.includes(string)'
'vip' in data.tags / data.name.contains('urgent') true
data.items.length > 0 runtime fault, No such key: length
size(data.items) > 0 / data.items.size() > 0 true
data.missing == 'x', !data.missing runtime fault, No such key: missing
has(data.missing) / has(data.missing) && data.missing == 'x' false, no fault
data.missing?.x / data.missing ?? 'd' parse fault
data.isAuthenticated && data.permissions.canEdit, !data.canPerformAction || data.isLocked, !data.hasPermission (the guide's other bare examples) evaluate, no fault

The fault policy quoted in the guide ("a visibility gate hides the node, disabled disables") is the one SchemaRenderer's visibilityFaultOutcome and #27584's changeset state.

Re-grep of skills/objectui/**, evals included, for ===, !==, &&, ||, .includes(, .length: the remaining hits are ${…} templates (which keep their own grammar), TypeScript / JSX code, or eval must_not_contain JSX strings. No other bare predicate uses a JS-only spelling.

Token ratchet (node scripts/check-skills-token-ratchet.mjs), priced tokens:

File Before After Ceiling
skills/objectui/guides/schema-expressions.md 6565 6562 6571
skills/objectui/rules/protocol.md 4366 4364 4368
skills/objectui/evals/schema-expressions.json 536 531 536

No ceiling row moves.

Gates: node scripts/pm/dispatch-gates.mjs --commands derives 45 families on this head; all 45 ran locally after a whole-tree turbo run build and exited 0; dispatch-gates --ran reconciles 45 derived / 45 run / 0 NOT-MEASURED.

Clause-②: no
Metadata-protocol: none

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind. (no names added)
  • 3. No formatter ran. (pnpm lint not run: no source file is touched; the derived gate families are listed under Verification.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (except the ## 维护者速读 block).
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_016WV4d1Ffw7D64ARzogMukj

Since #27584 the console reads a bare-string predicate as CEL, the way the
spec defines it and the server reads it. skills/objectui still taught the
legacy JavaScript spellings (`===`, `!==`, a JS operator list counted as
condition syntax), which now fault and hide the node.

- Every bare-predicate example is CEL; the architecture overview says a
  bare condition string is CEL on @objectstack/formula, and the operator
  list says it is the `${…}` grammar only.
- The Condition fields section gains a FROM → TO list measured on the
  console's evaluator: `===`/`!==`, `.includes`, `.length`, and a `data.*`
  key the scope may lack (`has()`; CEL faults on a missing key).
- The row-predicate bullet says only a `${…}` condition still reads on the
  legacy engine.
- Eval 2 now hands over a JS-syntax predicate and expects the CEL rewrite.
- Paid down in the same file: the bindable-text-keys paragraph restated
  troubleshooting item 1. All three files stay under their ceilings.

Fixes #27587

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4d1Ffw7D64ARzogMukj
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Oct 30, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

Contract-review-claim: session_016WV4d1QkPVVzNz3FJqb2iS · head e8ba3a8 · 2026-10-30T02:44Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e8ba3a8b984ea6c3ca01ef8169991201588a67a9
Local-runs: none
Scope: skills/objectui/guides/schema-expressions.md, skills/objectui/rules/protocol.md, skills/objectui/evals/schema-expressions.json + ui/packages/core/src/evaluator/ExpressionEvaluator.ts, ui/packages/core/src/evaluator/predicateInput.ts, ui/packages/core/src/evaluator/listConditional.ts, ui/packages/react/src/SchemaRenderer.tsx, packages/formula/src/cel-engine.ts, packages/spec/src/shared/expression.zod.ts, scripts/check-skills-token-ratchet.mjs, ui/scripts/check-skill-eval-tokens.mjs, .changeset/27587-objectui-skill-teaches-cel-predicates.md

Read: PR body, card #27587, the four-file diff origin/main...e8ba3a8b, the head's check-runs, the dev report, and the cited code on origin/main (the tree PR #27584 / 4c5023ac4 shipped). Nothing built or run.

① Derived judgments

  1. Bare string is CEL on the console — TRUE against the tree. ExpressionEvaluator.evaluateCondition (main): a string containing ${ goes to this.evaluate (legacy template path); every other bare string reaches if (!unwrappedEnvelope) return this.evaluateCelCondition(trimmed, options), which runs evalFieldPredicate on @objectstack/formula. toPredicateInput emits { dialect: 'cel', source } for a bare string. evalRowPredicate in listConditional.ts routes only source.includes('${') to the legacy engine; everything else is CEL. The spec side matches: ExpressionInputSchema string arm transforms to { dialect: 'cel', source }.
  2. "${…} templates keep their own grammar" — TRUE. The ${ branch above calls the legacy evaluator, whose SafeExpressionParser lexes === / !== (lines 321–345), typeof, ?., ?? and JS method calls. The guide's "Supported operators (full list)" header now scopes that list to the ${…} grammar, which is what the code does.
  3. FROM → TO rows checked against @marcbachmann/cel-js 8 under CEL_ENV_OPTIONS (unlistedVariablesAreDyn, enableOptionalTypes) plus the ObjectStack stdlib:
    • === / !== → == / !=: CEL has no ===; parse fault. Correct.
    • list.includes(x) → x in list; s.includes('x') → s.contains('x'): includes is not a CEL overload (the cel-error-classification test pins "found no matching overload" for an unknown overload); in and contains are standard CEL. listConditional.ts itself names .contains() as the CEL spelling. Correct.
    • list.length → size(list): .length on a list is a field select and faults "No such key" (pinned shape {"a": 1}.b in cel-error-classification.test.ts); size is pinned in cel-engine.test.ts. Correct.
    • a missing data.* key → has(data.x) && data.x == 1: a missing map key is no_such_key; has(record.a) is pinned in cel-engine.test.ts, has(data.environments[0].storage.usedMb) in cel-has-indexed-receiver.test.ts. Correct for the positive form; see should-fix 1 for the negated form.
    • ?. / ?? listed as JS-only: both fault on CEL (cel-js optional syntax is .?, not ?.); they stay in the ${…} list. Correct.
  4. The fault policy sentence "a visibility gate hides the node, disabled disables" — TRUE. SchemaRenderer.visibilityFaultOutcome: a HIDE key answers true (concealment), a declared SHOW key answers false (withholding); the disabled leg keeps evaluateCondition's fail-soft true, i.e. greyed out (docblock at line 1518 ff., objectui#6445). Also what fix(ui-core): read a bare-string predicate as CEL shorthand, as the spec and server do #27584's changeset states.
  5. Row-predicate bullet: "only a ${…} condition still routes to the old engine. === or .includes() faults on CEL. Both warn once" — TRUE. evalRowPredicate: isLegacyDialectSource warns once for any legacy marker (${, ===, .includes( …); only ${ is routed legacy; the rest faults on CEL into the fallback with warnEvalError (also once).
  6. protocol.md row "hidden": "data.status == 'draft'" and visibleWhen "(bare string = CEL)" — consistent with 1. "visible": "${data.isActive}" row unchanged and still a template; correct.
  7. Every bare predicate string left in the bundle on the head is valid CEL: data.status != 'active', data.isAuthenticated && data.permissions.canEdit, !data.canPerformAction || data.isLocked (guide), data.permissions.canView, data.status == 'draft', !data.hasPermission (protocol.md). Grep of skills/objectui/** on the head for ===, !==, .includes(, .length, ?., ??: every remaining hit is inside a ${…} template (guide lines 245, 270, 276, 632; auth-permissions.md:293; page-builder.md:44; testing.md:158), TypeScript/JSX code, the ${…} operator list itself, the new FROM → TO rows, or an eval must_not_contain JSX string. No JS-only spelling survives in a bare predicate. Nothing missed.
  8. Every stale "node conditions run on the recursive-descent parser" claim is gone: the field-level section's dash clause, the architecture overview's "both are parsed by", and troubleshooting item 4. The three surviving mentions of the recursive-descent parser (lines 9, 14, 531) describe the ${…} path only, which is right.
  9. Removals are restatement only: the bindable-text-keys paragraph (old lines 58–62) is word-for-word troubleshooting item 1 and rules/protocol.md "Rule: Bindable Text Keys"; the dropped "the On spellings are refused" tail of item 4 stays at guide line 262 and protocol.md line 24. Nothing taught was lost.
  10. Card coverage: all five lines the card names (protocol.md:17; guide line 10, 83–89, 254, 335–336) are rewritten, and the FROM → TO list the card asks for is present. Done.

② Semver level

@objectstack/skills: patch — correct. packages/skills is @objectstack/skills, the package that publishes the bundles; prior skills-doc changesets use the same line (e.g. .changeset/26475-skills-teach-queues-assignment-rules.md). Docs and one eval only; no schema, key or behaviour change. Clause-②: no / Metadata-protocol: none present on the PR body and in the changeset and agree with the diff. Not a narrowing (the narrowing was #27584's).

③ Boundary flags

  • Author: objectstack-fleet[bot]; no second login to name.
  • Dev deviation "eval 2 adjusted, no new eval (zero headroom)": accepted. The eval file sits at 536/536 on main; a new case would need a ceiling raise. Eval 2 remains a fair test: the prompt hands over data.role === 'admin' && data.status === 'active' with "never shows", which is exactly the withholding verdict visibilityFaultOutcome gives a declared visibleWhen that faults. The must_contain tokens (CEL, status == 'active', &&, ${) are all taught in the bundle (data.status == 'active' at guide line 10 satisfies the whole-token rule, . before s), echoing the prompt cannot satisfy status == 'active' (the prompt spells ===), and must_not_contain: props.label still refuses the wrong owner-name placement. It rewards the CEL answer and only that.
  • Dev deviation "check:skill-docs / check:skill-refs not derived, not run": noted; no path-name or reference changed in the Key-files list, and CI's skills gates cover the head.
  • Token ratchet: ceilings read from scripts/check-skills-token-ratchet.mjs are 6571 / 4368 / 536. Priced-byte arithmetic (ceil(bytes/4)) on both trees reproduces the dev's table exactly: guide 26260→26246 bytes = 6565→6562; protocol.md 17464→17456 = 4366→4364; eval 2144→2122 = 536→531. No ceiling row moved. Consistent.
  • CI at read time (run 40993314750 on e8ba3a8b): Build UI Deps and Dogfood Regression Gate success; Build Core, Lint UI, Type Check UI, Build and E2E UI, Live E2E UI and Test UI 1–16 in progress; Test Core 1–2 queued; UI Published Artifacts, Build Docs, Temporal Conformance, Dogfood Verify CLI and the matrix Dogfood gate skipped. No failure recorded; not yet green. Landing waits on all checks green per the landing rule.
  • open_questions: none raised by the dev.

④ Design judgment

  • guides/schema-expressions.md: holds against the code it cites and the card's scenario. An agent following the head's guide writes data.status == 'active' for a bare gate and ${…} for a template; both reach the right engine on main. The FROM → TO list is the measured, minimal redirection the card asked for, and the "Supported operators" header stops the JS list from being read as condition syntax, which was the root defect. The one imprecision is should-fix 1 below.
  • rules/protocol.md: the two row edits make the evaluated-fields table agree with evaluateCondition; no rule weakened, no gate affected.
  • evals/schema-expressions.json: eval 2 now grades the exact failure the card describes and the exact fix the guide teaches; see ③.
  • Nothing in the diff changes what the spec, the server or the console accept.

Must-fix: none.

Should-fix:

  1. Guide line 259–260: the FROM → TO row lists !data.x among the JS spellings but gives only the positive rewrite has(data.x) && data.x == 1. The negated intent ("x absent or falsy") is !has(data.x) || !data.x; an agent who pattern-matches has(data.x) && !data.x gets false when the key is absent, the opposite verdict. One clause naming the negated form would close it; fits in the file's 9-token headroom.

Implemented-by: claude/project-thread-7yo6br
Reviewed-by: session_016WV4d1QkPVVzNz3FJqb2iS

VERDICT: PASS

A key the scope may lack, read negated, is `!has(data.x) || !data.x`;
`has(data.x) && !data.x` answers false when the key is absent.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WV4d1Ffw7D64ARzogMukj
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

Contract-review-claim: session_016WV4d1QkPVVzNz3FJqb2iS · head 0376f2d · 2026-10-30T03:22Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0376f2de88506f547342451a064d3f6c4f2ec3e4
Local-runs: none
Scope: skills/objectui/guides/schema-expressions.md, skills/objectui/rules/protocol.md, skills/objectui/evals/schema-expressions.json + ui/packages/core/src/evaluator/ExpressionEvaluator.ts, ui/packages/core/src/evaluator/predicateInput.ts, ui/packages/core/src/evaluator/listConditional.ts, ui/packages/react/src/SchemaRenderer.tsx, packages/formula/src/cel-engine.ts, packages/spec/src/shared/expression.zod.ts, scripts/check-skills-token-ratchet.mjs, ui/scripts/check-skill-eval-tokens.mjs, .changeset/27587-objectui-skill-teaches-cel-predicates.md

Delta review by the same reviewer, following record 6513743666 (PASS on e8ba3a8b with one should-fix). Read: git diff e8ba3a8b..0376f2de, the whole diff origin/main...0376f2de, the head's check-runs, and the cited code on origin/main. Nothing built or run. The ① findings of the prior record stand for every line the delta does not touch; they are restated in brief so this record is complete on its own.

① Derived judgments

  1. The delta is exactly one hunk in skills/objectui/guides/schema-expressions.md (2 insertions, 1 deletion): FROM → TO row 4 now reads has(data.x) && data.x == 1, !has(data.x) || !data.x: CEL faults on a missing key. No other file, line or changeset moved; the whole diff against main is still the same four files (40+/24-).
  2. The added negated rewrite is correct against the CEL evaluator (@marcbachmann/cel-js 8 under CEL_ENV_OPTIONS, via evaluateCelCondition → evalFieldPredicate): has(data.x) with an identifier-rooted selection is cel-js's native macro (pinned in cel-engine.test.ts and cel-has-indexed-receiver.test.ts, including has(dyn(m).b) → false for an absent key). Absent key: !has is true and CEL's || is error-absorbing, so the verdict is true without faulting on data.x. Present false: !has false, !false true → true. Present true: false || false → false. This matches the thread's three measurements and closes the inversion the should-fix named (has(data.x) && !data.x would answer false on an absent key).
  3. Carried forward from 6513743666, unchanged on this head: a bare string without ${ reaches evaluateCelCondition and toPredicateInput emits { dialect: 'cel', source } (ExpressionEvaluator.ts, predicateInput.ts); ${…} goes to the legacy SafeExpressionParser, which owns ===, !==, ?., ??, typeof; evalRowPredicate routes only ${ legacy and warns once on any legacy marker; the spec's ExpressionInputSchema string arm transforms to the same envelope; the other FROM → TO rows (==/!=, x in list, s.contains(), size(list)) are standard CEL and the JS spellings fault as stated; the fault sentence (visibility gate hides, disabled disables) matches visibilityFaultOutcome and the disabled leg's fail-soft true; every remaining bare predicate string in the bundle is valid CEL and every other JS-spelling hit is a ${…} template, code, the ${…} operator list, the FROM → TO rows, or an eval must_not_contain string; removals are restatement only; all five card lines are rewritten.
  4. The Scope: set is unchanged: the delta cites no new code.

② Semver level

@objectstack/skills: patch — unchanged and still correct; the delta adds one clause of prose. Clause-②: no / Metadata-protocol: none unchanged on the PR body and in the changeset.

③ Boundary flags

  • Author: objectstack-fleet[bot]; no second login to name.
  • Token ratchet: the thread reports the guide at 6569/6571 with no row moved. Priced-byte arithmetic (ceil(bytes/4), the method the gate's own comments state) on 0376f2de: guide 26275 bytes → 6569 (ceiling 6571, headroom 2); rules/protocol.md 17456 → 4364 (4368); eval 2122 → 531 (536). Reproduces the thread's reading; no ceiling row changed in scripts/check-skills-token-ratchet.mjs.
  • CI on 0376f2de (runs 40995205904 / 40995205932 / 40995205937 / 40995205966 / 40995206203 / 40995205934 / 40995206136), read from the check-runs API: 56 check runs, 48 success, 8 skipped (Build Docs, Dogfood Verify CLI, Temporal Conformance, UI Published Artifacts, the Dogfood matrix shard, 18.x upgrade harness, Packed-tarball smoke, Schedule gate), 0 failed, none pending. Check Changeset, Governed Surface Queue Guard, Lint · docs and code guards, Lint · repo and tooling gates and all Test UI / Test Core shards are green. mergeable_state: clean. The PR is still a draft.
  • Prior should-fix 1 (negated missing-key rewrite): resolved by this delta.
  • Dev deviations from the first record (eval 2 adjusted rather than added; check:skill-docs / check:skill-refs not derived locally) stand as accepted; the docs and code guards lint job is green on this head.
  • open_questions: none raised.

④ Design judgment

  • guides/schema-expressions.md: the FROM → TO row now gives both directions of the missing-key rewrite, so an agent pattern-matching either data.x == 1 or !data.x from a scope that may lack x lands on a predicate the console and the server read identically. The guide holds against the cited code and the card's eval scenario; nothing else in the file moved.
  • rules/protocol.md, evals/schema-expressions.json: untouched by the delta; the prior ④ findings stand.
  • Nothing in the diff changes what the spec, the server or the console accept.

Must-fix: none.

Should-fix:

  1. PR body, "What changes" bullet for the FROM → TO list, still names only has(data.x) && data.x == 1 for row 4; the head's guide now also teaches !has(data.x) || !data.x. A one-clause body edit keeps the description equal to the tree. Cosmetic, outside the governed faces, not a landing blocker.

Implemented-by: claude/project-thread-7yo6br
Reviewed-by: session_016WV4d1QkPVVzNz3FJqb2iS

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-elon-musk · session_016WV4d1QkPVVzNz3FJqb2iS

维护者速读

  • 改了什么:skills/objectui 里所有不带 ${…} 的条件字符串都改成 CEL 写法(==、!=、!、&&)。运算符表说明:裸条件字符串是 CEL,${…} 模板保留自己的语法。指南加了一张简短的 FROM → TO 表,覆盖常见 JS 写法:===/!==、.includes、.length,以及缺失的 data.* 键(has(data.x) && data.x == 1,取反写 !has(data.x) || !data.x)。eval 2 改为考「JS 写法的谓词要改成 CEL」。4 个文件 +40/−24,含 @objectstack/skills patch changeset。
  • 为什么改:PR fix(ui-core): read a bare-string predicate as CEL shorthand, as the spec and server do #27584 之后,控制台和服务端一样把裸条件字符串当 CEL 求值,=== 等 JS 写法会求值失败并把节点藏起来;技能还在教 JS 写法,照着写的界面会静默消失。
  • 风险与回滚:只改技能文字和一条 eval,不改 spec、服务端或控制台的行为;三个文件都在 token 棘轮上限内,棘轮行没动。revert 即回滚。PR 正文「What changes」里第 4 行只写了正向写法,以本条为准。
  • 先看:skills/objectui/guides/schema-expressions.md 第 254–261 行的 FROM → TO 表,再看 rules/protocol.md 第 16–17 行。
  • 两分钟验证:git grep -n "===" -- skills/objectui/rules/protocol.md 应无命中;git grep -n "!has(data.x)" -- skills/objectui 应命中指南一处。
  • 需要你做的:skills/** 是受管面,登记册判 Tier H,请批准本 PR(契约复核 PASS 6513743666,差量 PASS 见上一条记录,Head-sha: 0376f2de88506f547342451a064d3f6c4f2ec3e4)。

席位意见:ACCEPT。首轮契约复核 PASS,有一条 should-fix(取反的缺键写法),已在本 PR 补上,差量复核 PASS。差量复核又提了一条 should-fix:PR 正文少写一个取反写法,属于正文描述问题,不在受管面上,由本条接替。CI 全绿。

席位核对

  • 形态: one PR, Fixes #27587, four files (+40/−24): skills/objectui/guides/schema-expressions.md, rules/protocol.md, evals/schema-expressions.json, and a @objectstack/skills patch changeset. Governed skills/**: check-governed-merges.mjs --pr 27889 reads Tier H (人合).
  • 前提实测: since PR fix(ui-core): read a bare-string predicate as CEL shorthand, as the spec and server do #27584 the console reads a bare-string predicate as CEL (evaluateCelCondition), so a JS-only spelling (===, .includes(, .length, ?.) faults; on origin/main the bundle taught data.status === 'draft' at protocol.md:17 and JS operators in the guide (claim 6513088634; review ①). Every remaining bare predicate in skills/objectui/** on the head is valid CEL (review ① item 7).
  • 门禁 check-runs: head 0376f2de88506f547342451a064d3f6c4f2ec3e4: 56 check-runs, 48 success, 8 skipped, 0 failed, 0 pending; Check Changeset, Governed Surface Queue Guard, lint/doc guards and every Test UI / Test Core shard success. Locally 45 derived gates, all exit 0.
  • 接受的偏差: eval 2 adjusted rather than a new case added (the eval file sat at its 536-token ceiling; the adjusted case grades exactly the card's failure); check:skill-docs / check:skill-refs not derived (no reference or path name changed). Token readings: guide 6569/6571, protocol.md 4364/4368, eval 531/536; no ratchet row moved.
  • 范围外去向: none. Delta should-fix 1 (the PR body's row-4 bullet) is superseded by this record; the relay cannot re-edit a PR body.
  • 落地条款: Tier H: a GOVERNED_APPROVERS approval, then the seat lands it under the maintainer's landing grant for this term (seat thread, 2026-10-29 22:04Z).

@os-zhuang
os-zhuang marked this pull request as ready for review October 30, 2026 03:27
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 30, 2026
Merged via the queue into main with commit 9122271 Oct 30, 2026
65 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-7yo6br branch October 30, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills/objectui teaches JavaScript-syntax bare predicates (===, !, raw JS operators) that the console will read as CEL after #27457

3 participants