Skip to content

docs(skills): objectstack-automation teaches the CEL value envelope in fields / assignment values and / 100.0 for money - #22284

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-22260-automation-skill-value-slot-cel
Oct 8, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-22260-automation-skill-value-slot-cel

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22260

Clause-②: no

skills/objectstack-automation/SKILL.md taught {token} values in create_record / update_record fields and the money sample {round(x * 100) / 100}. Since #19939 pass 1 landed (PR #22259 is merged; its changeset .changeset/19939-flow-value-slot-template-dialect-refused.md is the FROM → TO authority for every spelling below) the tree refuses those values at objectstack validate, registerFlow and the executors, and / 100 is integer division in CEL (#11182 ruling D item 2 prescribed / 100.0). #19939's second half (the two kept spellings) remains open. Tier H (skills/**): this PR stays draft and lands on an authorized APPROVED review or by the maintainer's hand — no seat readies, queues or arms it.

What changed — two files, skills/objectstack-automation/ only

SKILL.md (+33 / −32 lines on origin/main c8bb3c8d9c):

  • Item 1 of the authoring-traps list (:224-241) is rewritten around the value slot: in create_record / update_record fields.* and an assignment value (its two legacy shapes too) a string is the literal text it spells, a {…} token is refused at the three doors naming its CEL spelling, and a computed value is { dialect: 'cel', source: '…' } — one worked has() form ('{record.owner}' → source: 'has(record.owner) ? record.owner : null', with "nothing / null / a default is now YOUR call"), the array-index form ('{record.tags.0}' → 'record.tags[0]'), the money form (source: 'round(x * 100) / 100.0' — CEL divides two integers as integers, so / 100 turns 123.46 into 123), and the two spellings a value slot still accepts ({NOW()} / {TODAY() ± N}, {$User.PATH}). A second paragraph names the slots pass 1 does not touch — text slots (notify title / message, script inputs, http url / body, …) and filter keep the single-brace template — and keeps the text-slot grammar and its two traps ({{x}} is the template-field dialect; an unknown call-position name fails the node at run time).
  • Item 2 (:245-246): fields: { ref: '{newRec}' } / '{newRec.id}' → fields: { ref: { dialect: 'cel', source: 'newRec' } } (writes the whole record object) / source: 'newRec.id'.
  • Item 3's example (:273): fields: { ai_category: '{ai.ai_category}', ai_sentiment: '{ai.ai_sentiment}' } → fields: { ai_category: { dialect: 'cel', source: 'ai.ai_category' } } (one field; the second was the same spelling twice).
  • Filter-tokens section (:207-208, :213-214): fields dropped from "in title, message, fields and url a bare {current_year_start} is a nonsense reference" (a fields string is now literal text, not a reference at all) and "write payload" dropped from "(message body, http url, write payload) only renders an empty string — a warning" (a fields {record.x} is now an error at validate, not a warning).

evals/flows-triggers-approvals.json (eval 5): "persists {recalc.discount} via fields" → "persists it with fields: { discount: { dialect: 'cel', source: 'recalc.discount' } }"; must_contain gains dialect: 'cel', must_not_contain gains {recalc, so the eval now pins the new spelling and refuses the old.

Every {…} value spelling in the package, judged against the changeset's refused list

File :line on c8bb3c8d9c Spelling Slot Verdict Now
SKILL.md:224-226 "Value fields on a node's config (fields, inputs, notify message/title, …) interpolate {token}" puts fields in the template set false for fields item 1 rewritten; inputs and notify text keys stay in the template list
SKILL.md:232 {round(x * 100) / 100} value-slot teaching refused (expression), and integer division source: 'round(x * 100) / 100.0'
SKILL.md:235 / :237 body: '{{ai_reply}}' / body: '{ai_reply}' slot unnamed in a value slot {ai_reply} is refused (path); in a text slot still the template folded into the text-slot paragraph ({{x}} is the template-field dialect)
SKILL.md:236-237 ticket: '$source.id' / ticket: '{source.id}' fields-shaped key {source.id} refused (path) retired; the rule survives as "a string is the literal text it spells"
SKILL.md:238-241 '{ROUND(x, 2)}' / '{Math.round(x)}' / '{(x).toFixed(2)}' slot unnamed in a value slot the {…} is refused at validate first; in a text slot still fails the node at run time kept for text slots, one spelling
SKILL.md:245 fields: { ref: '{newRec}' } update_record field value refused (path) fields: { ref: { dialect: 'cel', source: 'newRec' } }
SKILL.md:246 fields: { ref: '{newRec.id}' } update_record field value refused (path) source: 'newRec.id'
SKILL.md:267 inputs: { ticketId: '{record.id}' } script.inputs not a value slot — screen-nodes.ts:343 still interpolates unchanged
SKILL.md:272 filter: { id: '{record.id}' } filter keeps the template unchanged
SKILL.md:273 fields: { ai_category: '{ai.ai_category}', ai_sentiment: '{ai.ai_sentiment}' } update_record field value refused (2 paths) one CEL envelope
SKILL.md:107-114 notify recipients / title / message / sourceId text slots keep the template unchanged
SKILL.md:154 "The handler reads {NODEID.error} (or run-wide {$error})" no slot named true in a text slot; in a value slot the refusal names vars["$error"].message unchanged (see Acceptance notes)
SKILL.md:208 / :213 "fields" / "write payload" in the filter-tokens prose — false since pass 1 dropped
references/examples-flows.md:41, :119-121 {TODAY()}, {TODAY() + N} filter keeps the template unchanged
references/examples-flows.md:44 fields: { status: 'escalated' } field value literal unchanged
references/state-machines-and-approvals.md:120 filter: { id: '{record.id}' }, fields: { stage: 'closed_won' } filter / literal field keep unchanged
evals/flows-triggers-approvals.json:7 renewal_date: { $lt: '{TODAY()}' }, fields: { status: 'lapsed' } filter / literal keep unchanged
evals/flows-triggers-approvals.json:17 notify recipients: '{record.owner_id}', "title with single-brace interpolation" text slots keep unchanged
evals/flows-triggers-approvals.json:47 "persists {recalc.discount} via fields" update_record field value refused (path) CEL envelope, pinned

references/_index.md is generator-owned and unchanged. No os:check-marked block changed (the two in this package are in references/*.md, untouched).

skills/** readings — the token ratchet, paid inside each file

File Before (c8bb3c8d9c) After (5ac59ff0da) Ceiling
SKILL.md 438 lines · 23125 bytes · 5782 tokens 439 lines · 23052 bytes · 5763 tokens 5785 (headroom 3 → 22)
evals/flows-triggers-approvals.json 55 lines · 5019 bytes · 1255 tokens 55 lines · 5018 bytes · 1255 tokens 1255 (headroom 0 → 2 bytes)
whole package, all 10 SKILL.md 4408 lines · 52572 tokens 4409 lines · 52553 tokens −19 tokens

Token = ceil(utf8 bytes / 4), the gate's own convention. No ceiling moved; nothing was re-wrapped to buy a line. Every deleted line and where its content survives:

  1. Item 1's grammar bullets ({var} / {record.title}, {record.tags.0} array index, {$User.Id} / {NOW()} / {TODAY() + 30}, the six functions "mirror the CEL stdlib 1:1", "anything without {…} is a literal") → compressed into the text-slot paragraph (same facts, now scoped to the slots that still read the template); "(e.g. a multiple: true lookup, stored as an array)" → "(array index)".
  2. "round is integer-only (no round(x, 2)); for N decimals write {round(x * 100) / 100} (scale 2)" → the money bullet (/ 100.0); round(x) → int is in objectstack-formula's stdlib table, which item 4 and the opening blockquote already point at.
  3. ❌ body: '{{ai_reply}}' → "{{x}} is the template-field dialect" in the text-slot paragraph.
  4. ❌ ticket: '$source.id' and ✅ body: '{ai_reply}', ticket: '{source.id}' → retired: in a value slot the ✅ spelling is now refused outright and the refusal names its CEL form; the literal rule survives as "a string is the literal text it spells" (value slots) and "no {…} ⇒ literal" (text slots).
  5. ❌ '{ROUND(x, 2)}' / '{Math.round(x)}' / '{(x).toFixed(2)}' "… with a named error naming the supported set. The build does not catch these (conditions are checked, call-position names are not)" → one spelling, the same rule ("fails the node at run time, unchecked at build, not fault-routable"), scoped to text slots — in a value slot the build refuses the {…} first.
  6. :277 the one-line defineStack({ functions: { 'helpdesk.aiTriageStub': … } }) registration — spelled a third time (item 3's head shows defineStack({ functions: { my_fn: … } }) and the second fence registers 'helpdesk.aiTriageStub') → deleted.
  7. :263 // ❌ DON'T: expect the function to update the record itself (it has no data API) → restated the prose two lines above the fence ("it does NOT read/write the database") → deleted; the ✅ line stays.

Eval file, paid by: "(FlowSchema has no top-level schedule; no cron tagged template)" → "(no top-level schedule; no cron tag)"; "nodes wired with edges (never next), ending in an end node" → "edges (never next), an end node"; "Failure routing is an edge { source, target, type: 'fault' }" → "a type: 'fault' edge" (the shape stays pinned by must_contain).

Measured at the tree, not recalled

Refusal control — built packages/spec/dist at 5ac59ff0da, flowNodeValueTemplateRefusals and UpdateRecordConfigSchema imported from @objectstack/spec/automation:

  • old :273 → 2 refusals, path=fields.ai_category / fields.ai_sentiment, label=update_record field value; each message leads with the VALUE_SLOT_TEMPLATE_REFUSAL sentence ("A value slot no longer reads the {…} template dialect: a string here is the literal text it spells …") then "Write {ai.ai_category} as { dialect: 'cel', source: 'ai.ai_category' }. CEL refuses an absent variable or key where the template wrote nothing, so guard one that may be absent with has(): has(ai.ai_category) ? ai.ai_category : null (the guarded form writes null)."
  • old :246 → 1 refusal at fields.ref: "Write {newRec.id} as { dialect: 'cel', source: 'newRec.id' } …"
  • old :232 → 1 refusal at fields.total: "Write {round(x * 100) / 100} as { dialect: 'cel', source: 'round(x * 100) / 100.0' }. Every division keeps a decimal operand: CEL divides two integers as integers, so round(x * 100) / 100 drops the decimals where round(x * 100) / 100.0 keeps them."
  • the new envelopes (ai.ai_category, newRec.id, round(x * 100) / 100.0, has(record.owner) ? record.owner : null) → 0 refusals; UpdateRecordConfigSchema.safeParse on the old sample → success: false at path ["fields","ai_category"], on the new sample → success: true.
  • the two survivors in fields ('{NOW()}', '{TODAY() + 30}', '{$User.Id}') → 0 refusals; notify title / message and script inputs carrying {record.id} → 0 (not value slots); legacy assignments: [{ variable, value: '{record.amount}' }] → 1 refusal at assignments[0].value.

CEL evaluation — built @objectstack/formula, ExpressionEngine.evaluate({ dialect: 'cel', source }, scope) with the engine's own celScope shape ({ extra: { ...vars, vars }, record: vars }, engine.ts:11791-11806), x = 123.456:

  • round(x * 100) / 100 ⇒ 123 · round(x * 100) / 100.0 ⇒ 123.46 · round(x * 100) ⇒ 12346
  • has(record.missing) ? record.missing : null ⇒ null · bare record.missing ⇒ error "No such key: missing" · record.tags[0] ⇒ "x" · has(vars.x) ? vars.x : null ⇒ 123.456
  • list[0] for a variable literally named list ⇒ error "Cannot index type 'type' with type 'int'" (list is a CEL type name) — see Acceptance notes.

Gates — local, at 5ac59ff0da

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on this tree derives 24 families from the two changed paths. All 24 ran, each exit code captured before any pipe; --ran reconciliation: "24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero — all 24 recorded an exit code and none of them is 3)".

Command Exit
node scripts/check-skills-token-ratchet.mjs — "skills/objectstack-automation/SKILL.md is 5763 tokens (ceiling 5785; headroom 22)" · "54 authored bundle file(s) within their ceilings" 0
node scripts/check-skills-token-ratchet.mjs --self-test 0
pnpm check:skill-identifier-liveness — "Leg 1: 457 citation(s) over 53 published file(s) … Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s)" 0
pnpm check:corpus-claim-drift — "Scanned: 254 .md/.mdx file(s) … skills 53" 0
pnpm check:doc-authoring 0
pnpm check:nul-bytes — "scanned 10237 text file(s) … no raw ASCII control bytes" 0
pnpm check:skill-compatibility — "11 pinned major(s) all match the workspace (@objectstack/spec is 17.x)" 0
pnpm check:skill-frame-sync 0
pnpm check:role-word 0
pnpm --filter @objectstack/spec run check:skill-docs — "Skill docs in sync" 0
pnpm check:watch-hint-literal 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions — first answered exit 3, "PREREQUISITE NOT MET — @objectstack/lint is not built" (not a measurement); after pnpm exec turbo run build --filter=@objectstack/lint --concurrency=2 under the verify lock (VERDICT command-exit 0) 0
node scripts/check-ci-filter-parity.mjs · check-closing-keyword-parity.mjs (+ --self-test) · check-comment-mask-corpus.mjs · check-doc-route-spelling.mjs --advisory (+ --self-test) 0 each
pnpm check:agent-test-spelling · check:cross-package-test-inputs · check:driver-memory-census · check:gitlink-declared · check:pm-governed-merges · check:refd-timer-probe 0 each

check:skill-examples not run as a control: no marked block changed. Not run locally (CI-owned): pnpm lint, the type-check lanes, Test Core — no package source changed. No package test / typecheck is owed: the diff touches no packages/**.

Changeset: none — skills/** publishes nothing from a released package; skip-changeset is applied with the PR assignee in one label-write call (recorded in the report comment on #22260).

Acceptance notes

  • Finding, for the seat to file (class c): the changeset's FROM → TO row '{list.0}' → { dialect: 'cel', source: 'list[0]' }, and celPath in packages/spec/src/automation/flow-value-slot-template.ts which every refusal message is built from, emit list[0] for a variable literally named list; under CEL list is a type name, so the remedy an author copies evaluates to "Cannot index type 'type' with type 'int'" (probe above). Affected: variables named after a CEL type (list, map, int, string, bool, double, uint, bytes, type, timestamp, duration, null_type). Reach: measured through the built formula engine, not through objectstack validate (whether validate accepts list[0] was not probed). Dedupe words: list[0] CEL type name, celPath value slot remedy, Cannot index type 'type', flow-value-slot-template list variable. The skill now teaches record.tags[0] instead.
  • Noted, not filed: SKILL.md:154 "The handler reads {NODEID.error} (or run-wide {$error})" names no slot; it holds in a text slot, and in a value slot the refusal names vars["$error"].message. Left as is (22 tokens of headroom); carrier: [v18] retire the {var} template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939's second half, which will touch this item again.
  • Noted, not filed: the frontmatter description still routes "CEL expressions in flow conditions / edge guards" to objectstack-formula; value envelopes now also are CEL. Untouched because the frontmatter regenerates skills/README.md and content/docs/ai/skills-reference.mdx (check:skill-docs), outside this card's file surface; carrier: none.

维护者速读(草稿)

改了什么

发布版自动化技能(skills/objectstack-automation/SKILL.md)里教 AI 怎么给流程节点的 fields 赋值的那一条,改成了现在运行时真正接受的写法:create_record / update_record 的 fields 和 assignment 的值,要么是字面量,要么是 CEL 信封 { dialect: 'cel', source: '…' };旧的 {token} 单花括号写法在这些位置会被 objectstack validate、registerFlow 和执行器拒绝。同时把金额取两位小数的样例从 / 100 改成 / 100.0,补了「键可能不存在时用 has() 守一下」的写法,并明确写出仍旧接受的两种旧写法({NOW()} / {TODAY() ± N}、{$User.Id})和没有变化的位置(通知文案、inputs、http、filter 仍用单花括号模板)。配套的一条 eval 也改成期望新写法。两个文件各自在自己的 token 上限内付清,没有抬上限。

为什么改

这份技能随 npx skills add 发到每个客户项目,是 AI 写流程之前读的第一份材料。PR #22259 合并后,它教的 fields 写法会在校验时被拒;更糟的是金额样例 {round(x * 100) / 100} 没有任何门拦着——在 CEL 里是整数除法,123.46 会悄悄变成 123。实测:旧样例在构建后的 spec 上全部被拒并给出 CEL 写法;新样例全部通过;CEL 引擎里 / 100 得 123、/ 100.0 得 123.46。

风险与代价(含回滚)

只改了技能文本和一条 eval,不碰 packages/**,不发包、不需要 changeset。风险在措辞:每句话都对照了合并后的 changeset 和源码,24 个派生门禁本地全绿(含 token 棘轮、标识符存活、兼容版本)。回滚就是 revert 这一个 commit,没有数据或运行时影响。一个顺带发现(变量恰好叫 list 时,拒绝文案给出的 list[0] 在 CEL 里跑不通)不在本 PR 范围,留给席位立单。

席位意见

(留空)

你要做的

审阅后在本 PR 上给一个 APPROVED(Tier H,skills/**),或自行合并;不需要其他操作。


Generated by Claude Code

…n fields / assignment values and / 100.0 for money

A flow value slot (create_record / update_record fields.*, an assignment
value) no longer reads the single-brace template dialect: a string is the
literal text it spells and a {…} token is refused at objectstack validate,
registerFlow and the executor, naming the CEL spelling. The skill's item 1,
item 2 and the script → update_record example now teach the
{ dialect: 'cel', source: '…' } envelope, the has() guard for a key that
may be absent, and / 100.0 for money (CEL divides two integers as
integers), and keep the two spellings a value slot still accepts
({NOW()} / {TODAY() ± N}, {$User.PATH}) and the text slots and filter that
keep the template. The eval that pinned {recalc.discount} in fields now
expects the envelope. Token ratchet paid inside each file.

Claude-Session: https://claude.ai/code/session_01CXydFDyiQwNbGFkmwrcRQq
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 8, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 8, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5ac59ff0da82eb668e631a2d7fffd2dc9b40918b
Local-runs: none

Reviewed in-seat by the skills seat 1 (dispatching seat; this session is served at the contract-review tier, read off get_session), 2026-10-08T12:22Z. Face hit: published skill text (skills/objectstack-automation/**, Tier H; dispatch-gates --tier MANDATORY). Read-only shape held: the diff (2 files, +33/−32), the card #22260 and its thread (the seat's unlock 6058882858, the claim 6058905926, the os-dev-report 6059461646), the head's check-runs; nothing built or run locally. Premises re-read on origin/main fbcbcf124f (it contains the branch base c8bb3c8d9c; skills/objectstack-automation/** is untouched between them; merge-tree clean).

① Derived judgments

  1. Slot boundary. The ledger on origin/main (packages/spec/src/automation/flow-node-expression-paths.ts, FLOW_NODE_EXPRESSION_PATHS) carries exactly three role: 'value' entries — assignment assignments.*, create_record fields.*, update_record fields.* — and flowNodeValueTemplateRefusals adds only the two legacy assignment shapes. The rewritten item 1 names those positions as the value slots and keeps notify title / message, script inputs, http url / body and filter on the single-brace template. Correct.
  2. Remedies are the judge's own strings (flow-value-slot-template.ts): celPath turns record.tags.0 into record.tags[0]; celExpression writes every integer divisor as a double (round(x * 100) / 100.0); guardOf emits has(record.owner) ? record.owner : null for a two-segment path. The skill's three worked forms are those strings. Correct.
  3. Kept spellings: KEPT_KINDS holds the date macro (NOW() / TODAY() with an optional ± N) and $User. paths, and a string carrying one of them is not refused. The skill's "still accepted" sentence names the same two and nothing else. Correct.
  4. Item 2 and the item-3 example now write envelopes (source: 'newRec' writes the record object, source: 'newRec.id' the id; ai.ai_category one field), and eval 5 pins dialect: 'cel' while refusing {recalc. The filter-tokens prose drops fields and "write payload", both false since PR feat(spec)!: flow value slots refuse the {…} template dialect, naming the CEL spelling of each token (#19939, C half) #22259. Correct.
  5. Ratchet, recomputed by the seat from the head blobs with the gate's convention (ceil of utf8 bytes over 4): SKILL.md 23052 bytes = 5763 tokens (ceiling 5785), the eval 5018 bytes = 1255 (ceiling 1255); no ceiling row changed. Payment is deletion of restated content (the ❌ / ✅ spellings whose value-slot form is now refused outright, the DON'T comment restating the prose two lines above it, the third defineStack({ functions }) registration), no re-wrap; each deleted fact survives where the PR body says. Correct.
  6. Scope: git diff --stat base..head = the two claimed files; no os:check block, no references/*.md, no packages/**. 24 gate families derived with dispatch-gates --commands, each exit captured, --ran reconciliation 24 derived / 24 run / 0 NOT-MEASURED; the one exit 3 (check:doc-formula-expressions, lint unbuilt) re-run to 0 after the build under the verify lock. The CI reading at this record's clock is in the ACCEPT on finding(skills): objectstack-automation SKILL.md teaches {token} values in create_record / update_record fields that PR #22259 (#19939 pass 1) refuses, and its money-rounding sample is CEL integer division #22260.

② Semver level

None. Published skill text and one eval; no package content, API or schema. skip-changeset is the correct declaration, applied by this seat after its own reading (the dev's label-write was refused by its session classifier and recorded in its report).

③ Boundary flags

Implemented-by: claude/issue-22260-automation-skill-value-slot-cel
Reviewed-by: session_01CXydFDyiQwNbGFkmwrcRQq

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

席位:skills 席 1,session_01CXydFDyiQwNbGFkmwrcRQq,2026-10-08T12:26Z。对照席位自己读的 diff(head 5ac59ff0da,两个文件 +33/−32)与 origin/main 上判官的源码(packages/spec/src/automation/flow-value-slot-template.ts、flow-node-expression-paths.ts)校正 dev 草稿;契约复核记录(PASS)在本 PR 上一条评论,ACCEPT 在卡片 #22260。

改了什么:发布版自动化技能(skills/objectstack-automation/SKILL.md)里教 AI 给流程节点 fields 赋值的那一条,改成运行时现在真正接受的写法:create_record / update_record 的 fields 和 assignment 的值,要么是字面量,要么是 CEL 信封 { dialect: 'cel', source: '…' };旧的 {token} 单花括号写法在这些位置会被 objectstack validate、registerFlow 和执行器拒绝(PR #22259 落地的 #19939 前半)。金额取两位小数的样例从 / 100 改成 / 100.0;补了「键可能缺席时用 has() 守一下」和数组下标 [0] 的写法;写明仍接受的两种旧写法({NOW()} / {TODAY() ± N}、{$User.Id})和没变的位置(通知文案、inputs、http、filter 仍用单花括号模板)。第 2、3 条的示例改成信封写法;配套 eval 第 5 条改成期望新写法并拒绝旧写法。两个文件都在各自 token 上限内付清(5763/5785、1255/1255),没抬上限。

为什么改:这份技能随 npx skills add 发到每个客户项目,是 AI 写流程前读的第一份材料;#22259 合并后它教的 fields 写法会在校验时被拒,而金额样例 {round(x * 100) / 100} 没有任何门拦着——在 CEL 里是整数除法,123.46 悄悄变成 123。dev 在构建后的 spec 上实测:旧样例全部被拒并给出 CEL 写法,新样例全部通过;CEL 引擎里 / 100 得 123、/ 100.0 得 123.46。

风险与代价(含回滚):只改技能文本和一条 eval,不碰 packages/**,不发包、无 changeset(skip-changeset)。风险在措辞:每句话对照了合并后的 changeset 与源码,24 个派生门禁本地全绿。回滚 = revert 落地的那一个 commit。

席位意见:同意合入,席内契约复核 PASS。席位自己在 origin/main 上核过三件事:值槽只有账本里三个 role: 'value' 位置(assignment / create_record / update_record)加 assignment 的两种旧形,文本划的边界与此一致;has()、[0]、/ 100.0 三种写法与判官的 guardOf / celPath / celExpression 逐字一致;仍接受的两种旧写法就是 KEPT_KINDS。顺带发现(变量恰好叫 list 时,拒绝文案给出的 list[0] 在 CEL 里是类型名、跑不通)由本席另立 spec 车道 finding 卡,不在本 PR。两处「看到但未改」(:154 的 {$error} 未指明槽位;frontmatter 仍只把条件里的 CEL 路由到 formula 技能)由 #19939 后半承接。

你要做的:用授权账号(os-zhuang 或 hotlong)在本 PR 上 APPROVE 一次,或亲手合入。PR 保持 draft;收到授权 APPROVED 后由本席落地并收口 #22260(若批准者已自行翻 ready 入队,本席只做收口)。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 8, 2026 12:32
@os-zhuang
os-zhuang enabled auto-merge October 8, 2026 12:32
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit ee6aa01 Oct 8, 2026
40 of 41 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-22260-automation-skill-value-slot-cel branch October 8, 2026 13:06
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 skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants