You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Deferred by ruling] Unify flow field-expression assignment onto the CEL engine — the B half of the #11060 ruling, awaiting maintainer appetite #11182
Ruled: 5805777944 · letter D · 2026-09-24T01:26Z
Blocked-by: #19938
Filed by the triage seat as the ruled follow-up of #11060 (maintainer 2026-08-23, live PM chat, verbatim: 「其他同意你的意见」— adopting "A now + B follow-up on hold pending appetite").
What this card is
#11060's ruled option A ships the six-function table (round/floor/ceil/abs/min/max, semantics mirrored from CEL) to unblock the quote flow. That is deliberately a bridge: it leaves the platform with TWO expression vocabularies — condition slots speak CEL (35 functions), assignment slots speak the template dialect (6). This card holds the end-state question: migrate assignment expressions onto the CEL engine so there is one dialect, one function set, declared = enforced.
Why it is on hold rather than queued
The migration cost is real and unmeasured — the {var} template dialect's compatibility surface across existing flows was the recorded confidence gap in #11060's analysis. Scheduling it is an appetite call the maintainer explicitly deferred.
(When A lands and #11060 closes, the unlock scan returns this card to the decision inbox for an appetite ruling — with A's landed shape and the by-then-measurable {var} dependency surface as inputs. ⛔ Do not implement from this card without that second ruling.)
Refs: #11060 (ruling + analysis) · hotcrm#1206 (the measured business pull).
Declared file face: the flow assignment-expression evaluation in packages/services/service-automation (the {var} template dialect and #11060's six-function table), plus the measurement across this repo's flows, examples/, and HotCRM.
Clause-② reading, with its citation: SKILL.md:524 negative boundary. Which evaluator a runtime slot uses is runtime behaviour, not a published contract face. ⚠️ If the work turns out to need a packages/spec change, it stops and the change goes to its own sub-card. The same applies to any change on the CEL engine side (packages/formula), as the ruling says. ⛔ This seat does not cross lanes.
Take basis: priority:p1, from the maintainer ruling on 2026-09-23 (5792259887), 「11182 当前版本就优先处理,统一为cel」. It is neither a tooling card nor a contract-hygiene card, so North Star clause 3 does not reach it.
Round shape, following the ruling's three points:
Measure first. Record who uses which {var} spelling today, with sites and counts.
Decide compatibility explicitly, never silently. If every measured spelling converts losslessly under an ADR-0087 conversion, the dev implements it with the conversion. If any spelling would break, the dev STOPS with a measured proposal, and this seat takes it back to the maintainer. ⛔ The code must not quietly choose.
os-dev-report
{
"issue": 11182,
"status": "needs_decision",
"branch": "claude/issue-11182-assignment-expressions-cel",
"pr": null,
"session": "session_01AhQASwqJr2Z7XfGWUdvnbF",
"premise_still_valid": true,
"summary": "MEASURE-FIRST round, stopped before any behaviour change per the ruling's point 2 because the measurement found spellings that break or change meaning. Measured surface (TS-AST walk of every inline flow node of type assignment / create_record / update_record, slots assignments.* + legacy shapes + fields.* incl. same-file spreads; objectstack @ 48c91e9, HotCRM @ 087b7c5dc4, read via anonymous clone): HotCRM src has 92 template-bearing assignment-slot sites, objectstack examples+src 21, and 106 of those 113 authored sites sit in create_record/update_record fields.. That slot has no CEL path today: the spec ledger declares only assignment.assignments. as a value-role envelope slot. Every measured spelling was evaluated through the shipped template dialect AND its best CEL conversion through the shipped CEL engine over the same variables (13 SAME / 12 DIFF). The silent-meaning break is the #11060 business pull itself. HotCRM quote-generation's 2 money sites round(... * 100) / 100 become CEL int/int division: 123.46 turns into 123 and 1111.1 into 1111, with no error. The 14 NOW()/TODAY()-family sites change type from ISO string to Timestamp, and CEL has no string(timestamp) overload to recover the string. Every path spelling flips from undefined to a fault when its key is absent. Two structural blockers sit outside this lane: the ADR-0087 conversion registry is packages/spec/src/conversions/registry.ts, and fields.* would need a spec ledger slot. Nothing was implemented, no PR was opened, and the pushed branch equals origin/main.",
"tests": "No code changed, so no gate is owed; the deliverable is the measurement. (1) Dependency closure built under the lock: pnpm --workspace-concurrency=2 --filter '@objectstack/service-automation^...' build printed os-verify-lock: VERDICT command-exit 0 · held the lock 302s. (2) Site census is a TypeScript-AST walk over git ls-files in both repos. Per-spelling counts (authored src/examples, tests separate): HotCRM src has bare {x} 31, dotted {a.b} 35, {$User.Id} 5, {NOW()} 7, {TODAY()} 3, {TODAY() + N} 3, {TODAY() + var} 1, {round(... * 100) / 100} 2 (quote-generation.flow.ts:107-108), {list.0} 1, embedded text 4, plus plain literals 38 and number/bool/null/const 22. HotCRM test has bare 1, dotted 3, NOW() 1, list.0 1, embedded 1, and 4 refusal probes (LOWER / TRIM / toLowerCase x2 in account-name-normalized-match.test.ts). objectstack examples+src has bare 9, dotted 10, {$User.Id} 1 (app-todo task.flow.ts:457), {$error.message} 1 (app-showcase flows/index.ts:1225), plus plain literals 12, array 1, bool 1. objectstack tests have bare 7, dotted 16, embedded 4, round-scale-2 1 (flow-field-expression-scale.integration.test.ts:146), list.0 1, and one CEL envelope. Zero sites anywhere: {$User.Email}, TODAY() - N in assignment slots, floor/ceil/abs/min/max, and ternary/compare/logical/string-literal expressions. (3) Template-vs-CEL probe (tsx over service-automation src interpolate() and @objectstack/formula ExpressionEngine, same scope shape as engine.ts celScope; scratch files deleted, not committed). SAME: resolved bare/dotted/list.0-to-[0]/$error-to-vars["$error"]/embedded-string/embedded-number/null value/plain literal as a quoted CEL string/round with an integer-valued amount (126000, which is why the #11060 oracle passes). DIFF: round(oppRecord.amount * (discount / 100) * 100) / 100 with amount 1234.56 and discount 10 gives template 123.46 against CEL 123, and total_price gives 1111.1 against 1111; 10 / 4 is 2 in CEL; {NOW()} is a string in the template dialect but now() is a Date; {TODAY()} is the string '2026-09-23' against today() returning a Date at midnight UTC; string(now()) has no overload for google.protobuf.Timestamp; {missing} resolves to undefined against Unknown variable: missing; {oppRecord.owner} with the key absent resolves to undefined against No such key: owner; {userList.0} on an empty list resolves to undefined against index out of bounds; embedded {o.name} null renders '' against no matching overload for string(null); current_user.id in the flow CEL scope gives Unknown variable: current_user because celScope binds no user; plain converted as CEL source gives Unknown variable: converted.",
"mcp_calls": "0 — no MCP GitHub tools used (reads went through REST curl; HotCRM through an anonymous git clone after add_repo returned read_available)",
"api_writes": "1 — POST /repos//issues/11182/comments (this os-dev-report, via scripts/pm/post-stamped.mjs); plus git push of the empty branch (not a REST write)",
"open_questions": [
{
"question": "Q1 (the gate question): measured spellings do NOT all convert losslessly, so how is compatibility for the existing template-dialect assignment values decided? The two HotCRM quote money sites change value silently under a verbatim CEL conversion, the 14 date-macro sites change type, and all path spellings flip from silent-undefined to a fault on absent keys.",
"options": [
"A: Additive unification. CEL envelopes become accepted in create_record/update_record fields.* too (the same #15137 envelope shape assignments.* already has), and the template dialect stays as-is for 17.x. A lint finding nudges authors to envelopes, and nothing existing changes meaning. Cost: spec sub-card (ledger slot + AssignmentValueSchema reach) plus an executor change in service-automation (M). Two dialects keep coexisting in value slots until v18 retires the template form. Anti-AI-error: CEL is available everywhere, but the old spelling is still accepted.",
"B: Converting unification now, in the ADR-0087 shape. A spec-side conversion rewrites every {…} value to a CEL envelope with semantic-preserving rewrites: integer literals become doubles (100 to 100.0), paths are wrapped for absent-key tolerance (has()/coalesce), list.0 becomes [0], and $User.Id becomes current_user.id with the flow CEL scope binding the user. Date macros need a packages/formula sub-card first (a string rendering of Timestamp, or date fields accepting Timestamp), otherwise 14 HotCRM sites change type. Cost: L, and three lanes (spec conversion + ledger, formula stdlib, service-automation executor/scope). The rewrites are semantic transforms, not renames, so they stretch ADR-0087 D2's 'losslessly mappable only' scope. The converted sources are also uglier than what an author would write.",
"C: Loud-refusal unification. The template dialect in value slots is refused at registration with a per-spelling prescription (the #11060 refusal shape), and authors migrate by hand. Cost: breaks all 92 HotCRM and 21 objectstack authored sites at once on a 17.x minor, which is a breaking change on a shipped slot mid-major and contradicts the ADR-0087 intent.",
"D: Hybrid. Do A now. Also fix the false 'round(x * 100) / 100 is the CEL idiom' guidance (see finding), and file the formula date-string sub-card. Put B's conversion plus C's refusal of the template form on the v18 train, where ADR-0087 majors are the conversion window."
],
"recommendation": "D. Four axes: (1) business pull: the quote flow works today on the template dialect, and A gives it CEL in fields.* with nothing broken; B's verbatim path would silently corrupt exactly that flow's money. (2) Long-term: one dialect is still the end state, and D reaches it at the only point where ADR-0087 sanctions a shape break (a major). The semantic rewrites B needs (int-to-double literals, absent-key wrapping) go beyond D2's rename-class scope. (3) Anti-AI-error: with A, AI authors can write CEL in every value slot today, and the template form can gain a lint 'prefer envelope' finding. C's 17.x mass refusal would red every existing flow. (4) Focus: A is one spec ledger sub-card plus one executor change. B needs three lanes, including packages/formula. Confidence gap: HotCRM is the only out-of-tree consumer measured, and third-party stacks are unmeasurable from here."
},
{
"question": "Q2 (boundary, independent of Q1): every option except C needs packages/spec changes (the conversion registry lives at packages/spec/src/conversions/registry.ts, and fields.* needs a value-role slot in the expression ledger resolveFlowNodeExpressions / builtin-node-config.zod.ts). Options B and D also need a packages/formula change for Timestamp-to-string. Both are out of this lane by the claim. Should the seat file the spec sub-card (and the formula one) as Blocked-by children of #11182?",
"options": [
"A: File 'spec: declare create_record/update_record fields.* as a value-role CEL envelope slot' (plus the ADR-0087 conversion entry if Q1=B) and 'formula: render a Timestamp to an ISO / date string in CEL' as sub-issues of #11182 in the objectstack repo, with #11182 Blocked-by both",
"B: Treat Q1's answer first and file only what it needs"
],
"recommendation": "B. File after Q1 is ruled. Under D only the spec ledger sub-card is needed now, and the formula one rides v18."
}
],
"out_of_scope_findings": [
"class: a · The shipped guidance tells authors that round(x * 100) / 100 is 'the CEL idiom' / 'identical in CEL' / 'matching the CEL authoring pattern', but under the CEL engine it is int/int division and truncates silently. Probe: round(12345.6) / 100 gives 123 and round(12345.6) / 100.0 gives 123.46. Copying the example into any CEL slot (assignment envelope, formula field) silently loses the cents. Sites: packages/services/service-automation/src/builtin/template.ts:83 (docblock) and :111 (the arity-refusal message text, shipped runtime), content/docs/automation/flows.mdx:1750, skills/objectstack-automation/SKILL.md:236, service-automation CHANGELOG 8155855 entry. In the template dialect itself the pattern is correct; only the 'same in CEL' claim is false. Seam: runtime:template.ts requireArity message → renderer: none; consumer: authors following the hint into CEL slots · dedupe words: 'round integer division CEL', 'round(x * 100) / 100', 'CEL idiom scale 2 truncation', '100.0 double literal'",
"carrier: the implementation round of #11182 (whichever Q1 option) will touch template.ts and flows.mdx and is the natural place to fix the wording to / 100.0, so the text is not repeated in two places · noted in the open question, not filed separately unless the seat prefers a standalone card"
]
}
Ruling: batch #218 item 1 · letter D · maintainer 「11182 D 其他同意」 2026-09-24T01:23Z
Director seat, summon #28 (续) (session_01GLdRPcbaCBQCTvVmU6YEUY). Presented in this seat's chat with recommendation E (A + C in the current line); the maintainer chose D — A now, the wrong guidance fixed now, B's conversion and C's refusal on the v18 train. E is ⛔ not taken; recorded as the maintainer's letter.
Governing text: the maintainer's 2026-09-23 word 「11182 当前版本就优先处理,统一为cel」 (5792259887), re-scoped by this letter — in 17.x 「统一」 means CEL usable in every value slot, one dialect arrives with v18; ADR-0087 D2 (only lossless mappings ride an automatic conversion — the 12 DIFF spellings the round measured are not lossless, which keeps B off 17.x); #11060 ruling A (the loud refusal shape). Readings: the seat's measurement 5795796051 / 5795832575 (113 sites, 106 in fields.*, 13 SAME / 12 DIFF); the three wrong-guidance sites re-read on origin/mainfdeeea0 — packages/services/service-automation/src/builtin/template.ts:83 and :111, content/docs/automation/flows.mdx:1757, skills/objectstack-automation/SKILL.md:236.
The wrong guidance is fixed now, in the first PR that touches each file: round(x * 100) / 100 is integer division in CEL — the text says so and prescribes / 100.0 — at template.ts:83 / :111 and flows.mdx:1757; skills/objectstack-automation/SKILL.md:236 is Tier H and rides its own draft for the maintainer's hand if the same PR cannot carry it.
needs-user-decision → pm:blocked in this stroke, Blocked-by: objectstack-ai/objectstack#19938 on the body; domain:services · p1 · Clause-②: no (runtime behaviour; the spec half carries its own declaration). The unlock scan returns this card when #19938 closes.
Claim: PM loop round 1 — member of the #19938 joint dispatch (ruling B)
Session: session_01CiCTczDo7tGhafXjf61dUJ
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-19938-fields-value-slot
Worktree: objectstack-issue-19938
Domain: domain:services (claimed by domain:spec seat 4 on the maintainer's ruling below)
Seat: domain:spec#4 (seat post #18917)
Ruling-ref: 5816929495
File surface: this card's engine half only, on the #19938 branch: packages/services/service-automation/src/builtin/crud-nodes.ts (evaluate a CEL value envelope in create_record / update_recordfields.*, as the assignment executor does for assignments.*), the value-envelope refusal wording in service-automation/src/engine.ts, builtin/logic-nodes.ts comments, builtin/config-expression-ledger.test.ts, builtin/template.ts (ruling D point 2: round(x * 100) / 100 is integer division in CEL — say so and prescribe / 100.0), and the author-time lint hint in packages/lint/src/validate-expressions.ts. The full joint surface is the Claim: on #19938 (5852841056). ⛔ Not skills/objectstack-automation/SKILL.md (Tier H; its own draft per ruling D point 2). (stop on breach; explain in the report)
Container & model: M/L, mode:subagent, model: default judgment tier, one dev for both halves
Clause-②: no
Thread-read: 5805777944
Serial constraints cleared: read at 2026-09-27T05:59Z. The joint census is on #19938's dispatch. No open PR touches packages/services/service-automation/** or validate-expressions.ts; draft PR #20170 touches only the front matter of content/docs/automation/flows.mdx.
Why the spec seat holds a domain:services card. The maintainer answered Q1 on #19938 with B (triage record 5816929495, maintainer verbatim 「19938 同意」): this card's engine commit and #19938's spec commit land on ONE branch in ONE PR, spec first, closing both cards together. That record states the ruling is the authorisation for the services-lane step. Blocked-by: objectstack-ai/objectstack#19938 in the body is dissolved by the fold, because both halves ship in the same merge. State: pm:blocked → pm:dispatched in this stroke. Ruling D (5805777944) still governs this card's content: A now; {var} keeps its 17.x meaning; a lint hint (warning) nudges toward the envelope; C and the lossless part of B ride the v18 carrier #19939.
domain:spec seat 4 (session_01CiCTczDo7tGhafXjf61dUJ), 2026-09-27T13:39Z: this card's engine half lands with #19938's spec half in PR #20205, per the maintainer's Q1 = B (5816929495 on #19938). The review of record is the ACCEPT 5856348996 on #19938. At-tier records: 5854753486 (PASS on the whole change) and 5856341116 (delta PASS at the landing head 56f77479e). The PR is ready with auto-merge armed, and this card closes on the merge through its Fixes #11182 line.
domain:spec seat 4 (session_01CiCTczDo7tGhafXjf61dUJ). PR #20205 merged through the merge queue on 2026-09-27T15:47:38Z, and both #19938 and #11182 closed completed through its Fixes lines. The readings that confirm the landing:
e46218674b has one parent (2bcd5cfeb7), is an ancestor of origin/main, and the queue branch for the PR is gone.
Its git patch-id --stable equals the reviewed diff's (9f9b9f52b952 on both; delta record 5857140844 at 68123f428, the third at-tier record on this change after 5854753486 and 5856341116).
By content: FlowValueSlotSchema appears in 5 files under packages/spec/src/automation/ at the merge, and in 0 on the parent.
The path to it: the PR was dequeued once on a dropped-refinements.baseline.json conflict, and two patch rounds re-merged main and recounted the ledger. Every non-generated file stayed byte-identical to the reviewed change.
pm:dispatched and the assignee come off both cards in the same act.
Downstream:#19961 (the next card on flow-node-expression-paths.ts) and #20168 (on schemaless-node-config.zod.ts) were serial behind this landing and are now takeable. skills/objectstack-automation/SKILL.md:236 (/ 100) stays with its own Tier H draft under ruling D point 2.
Activity
objectstack-fleet commentedon Sep 23, 2026
Claim:
domain:servicesPM seat · 2026-09-23T13:19ZSeat: domain:services#1
Session: session_01AhQASwqJr2Z7XfGWUdvnbF
Branch: claude/issue-11182-assignment-expressions-cel
Thread-read: 5792259887
Clause-②: no
Declared file face: the flow assignment-expression evaluation in
packages/services/service-automation(the{var}template dialect and #11060's six-function table), plus the measurement across this repo's flows,examples/, and HotCRM.Clause-② reading, with its citation:⚠️ If the work turns out to need a
SKILL.md:524negative boundary. Which evaluator a runtime slot uses is runtime behaviour, not a published contract face.packages/specchange, it stops and the change goes to its own sub-card. The same applies to any change on the CEL engine side (packages/formula), as the ruling says. ⛔ This seat does not cross lanes.Take basis:
priority:p1, from the maintainer ruling on 2026-09-23 (5792259887), 「11182 当前版本就优先处理,统一为cel」. It is neither a tooling card nor a contract-hygiene card, so North Star clause 3 does not reach it.Round shape, following the ruling's three points:
{var}spelling today, with sites and counts.NOW()/TODAY()— every other identifier is rewritten tonull, so a computed money value can never be rounded to its field's declaredscale#11060 A's shape. It never falls into a lenient default.⛔ Zero
packages/specin this lane.Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
os-dev-report
{
"issue": 11182,
"status": "needs_decision",
"branch": "claude/issue-11182-assignment-expressions-cel",
"pr": null,
"session": "session_01AhQASwqJr2Z7XfGWUdvnbF",
"premise_still_valid": true,
"summary": "MEASURE-FIRST round, stopped before any behaviour change per the ruling's point 2 because the measurement found spellings that break or change meaning. Measured surface (TS-AST walk of every inline flow node of type assignment / create_record / update_record, slots assignments.* + legacy shapes + fields.* incl. same-file spreads; objectstack @ 48c91e9, HotCRM @ 087b7c5dc4, read via anonymous clone): HotCRM src has 92 template-bearing assignment-slot sites, objectstack examples+src 21, and 106 of those 113 authored sites sit in create_record/update_record fields.. That slot has no CEL path today: the spec ledger declares only assignment.assignments. as a value-role envelope slot. Every measured spelling was evaluated through the shipped template dialect AND its best CEL conversion through the shipped CEL engine over the same variables (13 SAME / 12 DIFF). The silent-meaning break is the #11060 business pull itself. HotCRM quote-generation's 2 money sites
round(... * 100) / 100become CEL int/int division: 123.46 turns into 123 and 1111.1 into 1111, with no error. The 14 NOW()/TODAY()-family sites change type from ISO string to Timestamp, and CEL has no string(timestamp) overload to recover the string. Every path spelling flips from undefined to a fault when its key is absent. Two structural blockers sit outside this lane: the ADR-0087 conversion registry is packages/spec/src/conversions/registry.ts, and fields.* would need a spec ledger slot. Nothing was implemented, no PR was opened, and the pushed branch equals origin/main.","tests": "No code changed, so no gate is owed; the deliverable is the measurement. (1) Dependency closure built under the lock:
pnpm --workspace-concurrency=2 --filter '@objectstack/service-automation^...' buildprintedos-verify-lock: VERDICT command-exit 0 · held the lock 302s. (2) Site census is a TypeScript-AST walk overgit ls-filesin both repos. Per-spelling counts (authored src/examples, tests separate): HotCRM src has bare {x} 31, dotted {a.b} 35, {$User.Id} 5, {NOW()} 7, {TODAY()} 3, {TODAY() + N} 3, {TODAY() + var} 1, {round(... * 100) / 100} 2 (quote-generation.flow.ts:107-108), {list.0} 1, embedded text 4, plus plain literals 38 and number/bool/null/const 22. HotCRM test has bare 1, dotted 3, NOW() 1, list.0 1, embedded 1, and 4 refusal probes (LOWER / TRIM / toLowerCase x2 in account-name-normalized-match.test.ts). objectstack examples+src has bare 9, dotted 10, {$User.Id} 1 (app-todo task.flow.ts:457), {$error.message} 1 (app-showcase flows/index.ts:1225), plus plain literals 12, array 1, bool 1. objectstack tests have bare 7, dotted 16, embedded 4, round-scale-2 1 (flow-field-expression-scale.integration.test.ts:146), list.0 1, and one CEL envelope. Zero sites anywhere: {$User.Email}, TODAY() - N in assignment slots, floor/ceil/abs/min/max, and ternary/compare/logical/string-literal expressions. (3) Template-vs-CEL probe (tsx over service-automation src interpolate() and @objectstack/formula ExpressionEngine, same scope shape as engine.ts celScope; scratch files deleted, not committed). SAME: resolved bare/dotted/list.0-to-[0]/$error-to-vars["$error"]/embedded-string/embedded-number/null value/plain literal as a quoted CEL string/round with an integer-valued amount (126000, which is why the #11060 oracle passes). DIFF:round(oppRecord.amount * (discount / 100) * 100) / 100with amount 1234.56 and discount 10 gives template 123.46 against CEL 123, and total_price gives 1111.1 against 1111;10 / 4is 2 in CEL;{NOW()}is a string in the template dialect butnow()is a Date;{TODAY()}is the string '2026-09-23' against today() returning a Date at midnight UTC;string(now())has no overload for google.protobuf.Timestamp;{missing}resolves to undefined againstUnknown variable: missing;{oppRecord.owner}with the key absent resolves to undefined againstNo such key: owner;{userList.0}on an empty list resolves to undefined against index out of bounds; embedded{o.name}null renders '' againstno matching overload for string(null);current_user.idin the flow CEL scope givesUnknown variable: current_userbecause celScope binds no user; plainconvertedas CEL source givesUnknown variable: converted.","mcp_calls": "0 — no MCP GitHub tools used (reads went through REST curl; HotCRM through an anonymous git clone after add_repo returned read_available)",
"api_writes": "1 — POST /repos//issues/11182/comments (this os-dev-report, via scripts/pm/post-stamped.mjs); plus git push of the empty branch (not a REST write)",
"open_questions": [
{
"question": "Q1 (the gate question): measured spellings do NOT all convert losslessly, so how is compatibility for the existing template-dialect assignment values decided? The two HotCRM quote money sites change value silently under a verbatim CEL conversion, the 14 date-macro sites change type, and all path spellings flip from silent-undefined to a fault on absent keys.",
"options": [
"A: Additive unification. CEL envelopes become accepted in create_record/update_record fields.* too (the same #15137 envelope shape assignments.* already has), and the template dialect stays as-is for 17.x. A lint finding nudges authors to envelopes, and nothing existing changes meaning. Cost: spec sub-card (ledger slot + AssignmentValueSchema reach) plus an executor change in service-automation (M). Two dialects keep coexisting in value slots until v18 retires the template form. Anti-AI-error: CEL is available everywhere, but the old spelling is still accepted.",
"B: Converting unification now, in the ADR-0087 shape. A spec-side conversion rewrites every {…} value to a CEL envelope with semantic-preserving rewrites: integer literals become doubles (100 to 100.0), paths are wrapped for absent-key tolerance (has()/coalesce), list.0 becomes [0], and $User.Id becomes current_user.id with the flow CEL scope binding the user. Date macros need a packages/formula sub-card first (a string rendering of Timestamp, or date fields accepting Timestamp), otherwise 14 HotCRM sites change type. Cost: L, and three lanes (spec conversion + ledger, formula stdlib, service-automation executor/scope). The rewrites are semantic transforms, not renames, so they stretch ADR-0087 D2's 'losslessly mappable only' scope. The converted sources are also uglier than what an author would write.",
"C: Loud-refusal unification. The template dialect in value slots is refused at registration with a per-spelling prescription (the #11060 refusal shape), and authors migrate by hand. Cost: breaks all 92 HotCRM and 21 objectstack authored sites at once on a 17.x minor, which is a breaking change on a shipped slot mid-major and contradicts the ADR-0087 intent.",
"D: Hybrid. Do A now. Also fix the false 'round(x * 100) / 100 is the CEL idiom' guidance (see finding), and file the formula date-string sub-card. Put B's conversion plus C's refusal of the template form on the v18 train, where ADR-0087 majors are the conversion window."
],
"recommendation": "D. Four axes: (1) business pull: the quote flow works today on the template dialect, and A gives it CEL in fields.* with nothing broken; B's verbatim path would silently corrupt exactly that flow's money. (2) Long-term: one dialect is still the end state, and D reaches it at the only point where ADR-0087 sanctions a shape break (a major). The semantic rewrites B needs (int-to-double literals, absent-key wrapping) go beyond D2's rename-class scope. (3) Anti-AI-error: with A, AI authors can write CEL in every value slot today, and the template form can gain a lint 'prefer envelope' finding. C's 17.x mass refusal would red every existing flow. (4) Focus: A is one spec ledger sub-card plus one executor change. B needs three lanes, including packages/formula. Confidence gap: HotCRM is the only out-of-tree consumer measured, and third-party stacks are unmeasurable from here."
},
{
"question": "Q2 (boundary, independent of Q1): every option except C needs packages/spec changes (the conversion registry lives at packages/spec/src/conversions/registry.ts, and fields.* needs a value-role slot in the expression ledger resolveFlowNodeExpressions / builtin-node-config.zod.ts). Options B and D also need a packages/formula change for Timestamp-to-string. Both are out of this lane by the claim. Should the seat file the spec sub-card (and the formula one) as Blocked-by children of #11182?",
"options": [
"A: File 'spec: declare create_record/update_record fields.* as a value-role CEL envelope slot' (plus the ADR-0087 conversion entry if Q1=B) and 'formula: render a Timestamp to an ISO / date string in CEL' as sub-issues of #11182 in the objectstack repo, with #11182 Blocked-by both",
"B: Treat Q1's answer first and file only what it needs"
],
"recommendation": "B. File after Q1 is ruled. Under D only the spec ledger sub-card is needed now, and the formula one rides v18."
}
],
"out_of_scope_findings": [
"class: a · The shipped guidance tells authors that
round(x * 100) / 100is 'the CEL idiom' / 'identical in CEL' / 'matching the CEL authoring pattern', but under the CEL engine it is int/int division and truncates silently. Probe:round(12345.6) / 100gives 123 andround(12345.6) / 100.0gives 123.46. Copying the example into any CEL slot (assignment envelope, formula field) silently loses the cents. Sites: packages/services/service-automation/src/builtin/template.ts:83 (docblock) and :111 (the arity-refusal message text, shipped runtime), content/docs/automation/flows.mdx:1750, skills/objectstack-automation/SKILL.md:236, service-automation CHANGELOG 8155855 entry. In the template dialect itself the pattern is correct; only the 'same in CEL' claim is false. Seam: runtime:template.ts requireArity message → renderer: none; consumer: authors following the hint into CEL slots · dedupe words: 'round integer division CEL', 'round(x * 100) / 100', 'CEL idiom scale 2 truncation', '100.0 double literal'","carrier: the implementation round of #11182 (whichever Q1 option) will touch template.ts and flows.mdx and is the natural place to fix the wording to
/ 100.0, so the text is not repeated in two places · noted in the open question, not filed separately unless the seat prefers a standalone card"]
}
Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
测量完成,兼容性需要维护者裁定 ·
pm:dispatched→needs-user-decision· 2026-09-23T13:35Zdomain:servicesPM 席(席位帖 #6021)。本轮按认领评论 5795584973 的形状跑到第 2 步就停下了:测出有写法会被破坏或改变含义。按裁定第 2 点,这种情况不能在代码里悄悄选。**没有改任何代码,没有开 PR。**测量全文见 os-dev 报告 5795796051。测到了什么(objectstack
48c91e9e46· HotCRM087b7c5dc4)create_record/update_record的fields.*,而这个槽位今天根本没有 CEL 路径:spec 的表达式台账只把assignment.assignments.*声明成 value-role 信封槽。quote-generation.flow.ts:107-108的round(... * 100) / 100,原样转成 CEL 后是整数除法,123.46 → 123,1111.1 → 1111,不报错。这恰好就是 Flow field expressions can call no function butNOW()/TODAY()— every other identifier is rewritten tonull, so a computed money value can never be rounded to its field's declaredscale#11060 当初要解决的那个业务流程。NOW()/TODAY()在模板方言里是 ISO 字符串,在 CEL 里是 Timestamp,而且 CEL 没有string(timestamp)可以转回去。undefined,CEL 直接报错。packages/spec/src/conversions/registry.ts;fields.*要有 CEL 路径,需要在 spec 台账里加一个槽位。两处都不归本车道。选项
fields.*也接受 CEL 信封(与assignments.*今天的形状相同),模板方言在 17.x 保持原样,再加一条 lint 提示作者改用信封。**没有任何现有写法改变含义。**代价:一张 spec 台账子卡,加一次引擎改动(M)。两套方言会并存到 v18。{…},并做语义保持的改写:100→100.0、缺失键包一层has()、$User.Id→current_user.id。日期类要先在packages/formula立子卡。代价:L 级,跨三条车道。这些改写是语义变换,不只是改名,已经超出 ADR-0087 D2「只做可无损映射的改写」的范围。附带发现(class a,不论选哪个选项都要修)
现行的文字指引说
round(x * 100) / 100是「CEL 的惯用写法」「在 CEL 里一样」,这是错的。在 CEL 里这是整数除法,会静默丢掉分;应当写成/ 100.0。出现的位置:service-automation/src/builtin/template.ts:83(docblock)与:111(运行时报错文案,已发布)、content/docs/automation/flows.mdx:1750、skills/objectstack-automation/SKILL.md:236。任何一个选项的实现轮都会碰到template.ts和flows.mdx,顺手修掉就行,所以本席不单独立卡。下一步
维护者裁定后,本席按裁定需要的范围立 spec 子卡(以及可能要的 formula 子卡),都挂
Blocked-by,然后把本卡放回队列。Generated by Claude Code
objectstack-fleet commentedon Sep 24, 2026
Ruling: batch #218 item 1 · letter D · maintainer 「11182 D 其他同意」 2026-09-24T01:23Z
Director seat, summon #28 (续) (
session_01GLdRPcbaCBQCTvVmU6YEUY). Presented in this seat's chat with recommendation E (A + C in the current line); the maintainer chose D — A now, the wrong guidance fixed now, B's conversion and C's refusal on the v18 train. E is ⛔ not taken; recorded as the maintainer's letter.Governing text: the maintainer's 2026-09-23 word 「11182 当前版本就优先处理,统一为cel」 (5792259887), re-scoped by this letter — in 17.x 「统一」 means CEL usable in every value slot, one dialect arrives with v18; ADR-0087 D2 (only lossless mappings ride an automatic conversion — the 12 DIFF spellings the round measured are not lossless, which keeps B off 17.x); #11060 ruling A (the loud refusal shape). Readings: the seat's measurement 5795796051 / 5795832575 (113 sites, 106 in
fields.*, 13 SAME / 12 DIFF); the three wrong-guidance sites re-read onorigin/mainfdeeea0—packages/services/service-automation/src/builtin/template.ts:83and:111,content/docs/automation/flows.mdx:1757,skills/objectstack-automation/SKILL.md:236.Ruled — D
create_record/update_recordfields.*accept the CEL expression envelope, the same shapeassignment.assignments.*accepts today. Contract first: the spec expression ledger declaresfields.*as a value-role envelope slot — filed as spec: declarecreate_record/update_recordfields.*as a value-role expression slot in the expression ledger, so the CEL envelope is accepted there (the contract half of #11182 ruling D) #19938 (domain:spec· p1 ·pm:queue,Clause-②: yes); the engine half (this card) lands after it. The{var}template dialect keeps its 17.x meaning unchanged; an author-time lint hint (warning) nudges toward the envelope. ⛔ No spelling changes meaning in 17.x.round(x * 100) / 100is integer division in CEL — the text says so and prescribes/ 100.0— attemplate.ts:83/:111andflows.mdx:1757;skills/objectstack-automation/SKILL.md:236is Tier H and rides its own draft for the maintainer's hand if the same PR cannot carry it./ 100.0,has()guards, a string form forNOW()/TODAY()) and whatever of B is lossless ride the v18 train — filed as [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 (domain:services·pm:on-hold·Restart-when: closed objectstack-ai/objectstack#11182), so the timing question returns to the box when A lands, ⛔ never dropped silently. HotCRM's 92 sites migrate against that card, behind its pin.Execution
needs-user-decision→pm:blockedin this stroke,Blocked-by: objectstack-ai/objectstack#19938on the body;domain:services· p1 ·Clause-②: no(runtime behaviour; the spec half carries its own declaration). The unlock scan returns this card when #19938 closes.objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1 — member of the #19938 joint dispatch (ruling B)
Session:
session_01CiCTczDo7tGhafXjf61dUJAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-19938-fields-value-slotWorktree:
objectstack-issue-19938Domain:
domain:services(claimed bydomain:specseat 4 on the maintainer's ruling below)Seat:
domain:spec#4(seat post #18917)Ruling-ref: 5816929495
File surface: this card's engine half only, on the #19938 branch:
packages/services/service-automation/src/builtin/crud-nodes.ts(evaluate a CEL value envelope increate_record/update_recordfields.*, as the assignment executor does forassignments.*), the value-envelope refusal wording inservice-automation/src/engine.ts,builtin/logic-nodes.tscomments,builtin/config-expression-ledger.test.ts,builtin/template.ts(ruling D point 2:round(x * 100) / 100is integer division in CEL — say so and prescribe/ 100.0), and the author-time lint hint inpackages/lint/src/validate-expressions.ts. The full joint surface is theClaim:on #19938 (5852841056). ⛔ Notskills/objectstack-automation/SKILL.md(Tier H; its own draft per ruling D point 2). (stop on breach; explain in the report)Container & model:
M/L,mode:subagent,model: default judgment tier, one dev for both halvesClause-②: no
Thread-read: 5805777944
Serial constraints cleared: read at 2026-09-27T05:59Z. The joint census is on #19938's dispatch. No open PR touches
packages/services/service-automation/**orvalidate-expressions.ts; draft PR #20170 touches only the front matter ofcontent/docs/automation/flows.mdx.Why the spec seat holds a
domain:servicescard. The maintainer answered Q1 on #19938 with B (triage record5816929495, maintainer verbatim 「19938 同意」): this card's engine commit and #19938's spec commit land on ONE branch in ONE PR, spec first, closing both cards together. That record states the ruling is the authorisation for the services-lane step.Blocked-by: objectstack-ai/objectstack#19938in the body is dissolved by the fold, because both halves ship in the same merge. State:pm:blocked→pm:dispatchedin this stroke. Ruling D (5805777944) still governs this card's content: A now;{var}keeps its 17.x meaning; a lint hint (warning) nudges toward the envelope; C and the lossless part of B ride the v18 carrier #19939.objectstack-fleet commentedon Sep 27, 2026
domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ), 2026-09-27T13:39Z: this card's engine half lands with #19938's spec half in PR #20205, per the maintainer's Q1 = B (5816929495on #19938). The review of record is the ACCEPT5856348996on #19938. At-tier records:5854753486(PASS on the whole change) and5856341116(delta PASS at the landing head56f77479e). The PR is ready with auto-merge armed, and this card closes on the merge through itsFixes #11182line.objectstack-fleet commentedon Sep 27, 2026
Landed — PR #20205 →
e46218674b(2026-09-27T15:52Z)domain:specseat 4 (session_01CiCTczDo7tGhafXjf61dUJ). PR #20205 merged through the merge queue on 2026-09-27T15:47:38Z, and both #19938 and #11182 closedcompletedthrough itsFixeslines. The readings that confirm the landing:e46218674bhas one parent (2bcd5cfeb7), is an ancestor oforigin/main, and the queue branch for the PR is gone.git patch-id --stableequals the reviewed diff's (9f9b9f52b952on both; delta record5857140844at68123f428, the third at-tier record on this change after5854753486and5856341116).FlowValueSlotSchemaappears in 5 files underpackages/spec/src/automation/at the merge, and in 0 on the parent.The path to it: the PR was dequeued once on a
dropped-refinements.baseline.jsonconflict, and two patch rounds re-mergedmainand recounted the ledger. Every non-generated file stayed byte-identical to the reviewed change.pm:dispatchedand the assignee come off both cards in the same act.Downstream: #19961 (the next card on
flow-node-expression-paths.ts) and #20168 (onschemaless-node-config.zod.ts) were serial behind this landing and are now takeable.skills/objectstack-automation/SKILL.md:236(/ 100) stays with its own Tier H draft under ruling D point 2.feat(spec,automation): create_record / update_record fields.* accept …
feat(spec)!: flow value slots refuse the `{…}` template dialect, nami…
docs(skills): objectstack-automation teaches the CEL value envelope i…
feat(formula): isoDate(t) / isoDatetime(t), the string form of a CEL …