Repository navigation
Description
Why this issue exists
The profile rule at pkg/flowstate/v1/celenv.go:670-686 is explicit: a parse-time macro whose expansion spells only vocabulary the profile already evaluates may join 2026.1; anything that declares a runtime name means a new profile. cel-go moved a long way past v0.31.0 in August 2026, and without a screened list the next profile will be assembled from whatever go get happened to bring (#1358's failure mode) rather than from a reviewed decision. This is the screen, applied row by row, with the Flowstate spelling or issue each row touches. Nothing here is admitted by this issue.
| Upstream | Status at f8478f1 |
Kind | Flowstate today | Screen |
|---|---|---|---|---|
json.parse(string) -> optional(dyn) and json.parse(string, T) -> optional(T) (cel-expr/cel-go#1461) |
open PR | runtime name in encoders |
json_parse(string|bytes) -> dyn (celenv.go:782-820), which #1454 says must die or json.encode must, under STYLE.md R3 |
The strongest candidate: it resolves #1454 by adopting the upstream dotted spelling, its optional return is the canonical absence spelling the DSL already settled on, and the typed variant is the parse-to-declared-shape #1452 and #177 want. New profile; flow fix rewrites json_parse(x) to json.parse(x).orValue(…) or refuses where no default is expressible. Upstream decodes numbers through its own path, which is where #1432's "integral JSON number decodes to int" decision must be checked before adoption. |
optional.hasValue(v) (cel-expr/cel-go#1448; cel/library.go:372-381, optional library version 3) |
merged | runtime overload | x.?y.hasValue() is canonical (STYLE.md:47-48) |
Sugar for opt.hasValue() ? opt.value() == v : false. Harmless, but a new overload on a name the profile already has, so it arrives only with a pin raise (#1358), never silently. |
hasOnly/hasAny/hasAll/hasExactly on lists (cel-expr/cel-go#1447) |
merged | runtime names in lists |
sets.contains/equivalent/intersects and comprehensions |
Readable set tests that overlap sets; admitting both is an R3 question ("one spelling") before it is a profile question. |
base64.encodeUrl/decodeUrl (cel-expr/cel-go#1451) |
merged | runtime names in encoders |
base64.encode/decode |
Needed the moment a workflow builds a URL-safe token by hand; small and unambiguous. New profile. |
HMAC compute/verify and linear-time equal (cel-expr/cel-go#1416, #1450); JWT parse and claim helpers (#1415); DPoP (#1437, open) — an ext/security package |
after v0.32.0 (not in the tagged module) | runtime names | Webhook HMAC verification is Go (webhookverify.go); secrets are unresolvable in expressions (eval.go:526-534); docs/VISION.md leaves "where verification helpers live — CEL function, task, or the capability contract" open |
An HMAC in CEL needs a key in CEL, which invariant 7 forbids in workflow code; the only sites where a key could be bound are activity-side (a webhook verify: evaluated in the server, an http expect:). Refused for the workflow dialect; a candidate for a policy or verify environment only. DPoP belongs to #1012. |
| Timestamp parsing helpers (cel-expr/cel-go#1414) | v0.32.0 | runtime names | timestamp(string) RFC 3339 only; #1436 wants type: timestamp at the boundary |
The boundary decision (#1436) lands first so the parse helpers have a declared type to produce. |
maps.merge (cel-expr/cel-go#1409, #1239) |
open | runtime name | map construction via comprehensions / transformMap |
A real ergonomic gap (merging two struct outputs); wait for upstream to pick one of the two PRs. |
| Int/double comparison without rounding (cel-expr/cel-go#1377), precision fix (#1328) | open | semantics | #1432's numeric model | Whatever #1432 decides for 1 == 1.0 must be the same answer at validate and run; upstream is moving on the run-time half. |
| Type aliasing for native types (cel-expr/cel-go#1463) | merged | declaration style | four native-type policy identities (#548 finding 1, #1429) | Lets the policy environments name their identity type once; a tool for #548's vocabulary unification, not a profile change. |
EnableIdentifierEscapeSyntax (present at v0.31.0) |
pinned | parser syntax | the four lexer-token step ids true, false, null, in are refused (docs/ARCHITECTURE.md, invariant 2) |
Backticked field names would make steps.`in`.value legal, and a compiled spec carries the field name, so a worker predating the option evaluates it. Passes the profile rule; must pass STYLE.md R1 (who needs a step called in?). Recorded so it is not rediscovered. |
Desired outcome
Each row has a recorded verdict — admit to the next profile, admit as a macro to 2026.1, refuse, or wait on a named upstream PR — and the next profile's membership is assembled from the admitted rows in one reviewed change, after #1358 and the cel-go bump.
Acceptance criteria
docs/DSL.mdrecords the verdicts as a round, with the refused rows and their reasons (invariant 7 for HMAC/JWT, R3 forhasOnlybesidesets).- cel:
json_parse(x)andjson.encode(x)are the profile's two spellings of the json namespace — one an underscore function, one a dotted one — and STYLE.md R3 says one dies #1454 is resolved by thejson.parserow's verdict rather than separately. - No runtime name in the table appears in
docs/reference/cel.mdfor2026.1after the bump.
Constraints
- Every "new profile" row waits on the cel-go bump issue and cel: eight of eleven profile libraries are version-unpinned — the strings pin's own doctrine, unapplied to the rest of 2026.1 #1358.
- Rows marked open upstream are not decided until merged; an admitted function is pinned by library version, never by "latest".
Activity
json_parse(x)andjson.encode(x)are the profile's two spellings of the json namespace — one an underscore function, one a dotted one — and STYLE.md R3 says one dies #1454picatz commentedon Sep 8, 2026
Current-source calibration for adoption and migration, checked against Flowstate b015dc6 and the already-tracked upstream JSON proposal at head
091deba01ac55f696daef251deeee8e382a95346. Keeping upstream discussion links centralized in this issue.json.parse(string) -> optional(dyn)andjson.parse(string, type(T)) -> optional(T); its examples pass a type identifier as the second argument. It is an extension proposal, not a promise that every CEL implementation supports the name.json_parsereturns dyn or an error and also accepts bytes. A mechanical.orValue({})or.orValue(null)invents application behavior. Preserve an author's explicit fallback if equivalence is proved; otherwise refuse with a positioned migration diagnostic. Do not independently rename under cel:json_parse(x)andjson.encode(x)are the profile's two spellings of the json namespace — one an underscore function, one a dotted one — and STYLE.md R3 says one dies #1454.ext/encoders.go,trackJSONParsereturnsmath.MaxUint64and static estimation is deliberately broad. Adoption requires a measured, bounded Flowstate cost rule. This is a source observation, not a reproduced Flowstate runtime failure.EncodersVersionis necessary but insufficient to freeze behavior: the draft also changes shared encoding implementation. Cover existingjson.encodebehavior, numeric boundaries, map encoding, bytes inputs, and error/optional/null distinctions with the semantic corpus in cel: profile 2026.1's semantics are pinned by a function listing and an examples corpus, not by the language's own conformance suite —cel.dev/expralready ships the textproto cases for every library the profile admits, and it is already ingo.mod#1509 before any dependency/profile change.Suggested acceptance addition: retain the old-profile corpus, evaluate it with the pre-upgrade and candidate interpreter, and run one typed parse through validate, local execution, Temporal execution, test, and tooling. Record deliberate differences under a new profile; use
flow fixonly where semantics are demonstrably preserved. The v0.32 module-path move tocel.dev/cel-gois also part of the dependency migration, not evidence thatjson.parsehas shipped.No dependency update or upstream code execution was performed in this research pass.