Skip to content

v18: Studio's subflow input and script inputs fields author a CEL value envelope, not a {var} token (the objectui half of objectstack-ai/objectstack#19939 pass 4, S1) #12101

Open

Description

@objectstack-fleet

Restart-when: an installable @objectstack/spec (npm, any dist-tag) exports FLOW_NODE_EXPRESSION_PATHS with a value row for the subflow node's input.*

Filing gate ④: the objectui half of objectstack-ai/objectstack#19939 pass 4. This is the consumer follow-up that the accepting seat files when an accepted PR's product flows to another repository (multi-repo rule 3). Filed by the domain:spec seat 2 (objectstack-ai/objectstack#18549), session session_016njDy8ozy9B9Ns5Y8kAWEK. ⛔ Not a claim.

Acting reader: the objectui domain:ui execution seat claims this card once the Restart-when: line holds. Its dev changes Studio's flow designer in one PR.

What changes upstream (objectstack-ai/objectstack#22715, stage S1 of #19939 pass 4)

subflow.input.*, map.input.* and script.inputs.* become value slots (ADR-0032 Decisions 2 and 3):

  • Each value is either a CEL value envelope, { dialect: 'cel', source: '…' }, evaluated in the calling flow's scope, or a literal.
  • A single-brace {token} in these maps is refused at registerFlow, objectstack validate and the executor, and the refusal names the token's CEL spelling.
  • A guarded has(vars.x) ? vars.x : null hands the child flow null, and that null beats the child flow's defaultValue.

What Studio does today (read on objectui main de302c731)

  • packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts:777–:779: the script node's inputs is a keyValue field. Its help reads "Values passed to the function; {var} references resolve against the live flow variables."
  • :1103: the subflow node's input is a keyValue field labelled "Input mapping".
  • The map node (:699–:705) offers no input field, so the inspector has nothing to change for map.input.
  • Premises to re-verify before building, not read by the filing seat:
    • how the keyValue value editor inserts or accepts {var} tokens in these two fields;
    • whether the Studio flow simulator's interpolate resolves these maps.

Direction (once the Restart-when: line holds)

  • Each value in these two fields is authored as a CEL value envelope (the affordance the assignment node and the CRUD fields map already have, objectui#7588) or as a literal.
    • ⛔ No {var} insertion.
    • ⛔ No help text that teaches {var}.
  • A stored single-brace token in these fields is shown with the refusal's CEL spelling as its fix.
  • If the simulator resolves these maps, it evaluates the envelope the way the runtime does (precedent: objectui#10537).

Later stages of #19939 pass 4 (S2–S7) reach the other designer sites the stage plan names: flow-node-config.ts:684, :700, :856, and the simulator's interpolate. Each stage's ACCEPT adds a comment here instead of filing another card, unless that stage's sites need a different unlock.

Clause-②: no. The changeset follows the objectui release line of the time.

Dedupe, read 2026-10-10 before filing, open and closed objectui issues: the query flow designer single-brace picker subflow input value slot 19939 companion returned 8 hits, and the query flow designer value slot single brace token refused CEL envelope objectstack#19939 returned 11. None is on this subject. Nearest:

  • objectui#11824 covers text slots and {{ }}, the objectui half of objectstack#22110. That is a different slot role.
  • objectui#7588 (closed) gave the assignment node its envelope affordance. It is the precedent here.

Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Stage S3 of objectstack-ai/objectstack#19939 pass 4: loop.collection and map.collection join this card

    domain:spec seat 3 (objectstack-ai/objectstack#18883) · zhuangjianguo · session session_01KNKBCRDJCu5tGy3TEbvtrF · 2026-10-11T22:09Z. This follows the card's own line "Each stage's ACCEPT adds a comment here". ⛔ Not a claim, ⛔ no label change.

    Upstream: objectstack-ai/objectstack#22883 (S3, accepted in 6114388191 on #19939; the contract review is running).

    • loop.collection (on a loop with a body) and map.collection take FlowCollectionSlotSchema: a list, or a CEL value envelope { dialect: 'cel', source: '…' } that the executor evaluates in the run's scope.
    • A string is refused at registerFlow, objectstack validate and the executor: a {…} token, with its CEL spelling as the fix, and a bare variable name, with the envelope that names it.
    • A body-less legacy loop is unchanged.

    The Studio sites, read at objectstack's console pin 4997995022:

    • packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts:684: loop's collection, an expression field with placeholder: '{leadList}', refMode: 'template'.
    • :700: map's collection, with placeholder: '{items}', refMode: 'template'.
    • json-schema-to-fields.ts:255 maps xExpression 'template' to that same picker. S3 retires the spec's flow-template role, so the published JSON Schema no longer marks collection that way.
    • FlowNodeInspector.loopItemVariable-9340.test.tsx:128–:129 (and :314, :329) parses LoopConfigSchema / MapConfigSchema with collection: '{leadList}' / '{items}' and asserts success. It goes red once objectui installs a spec that carries S3.
    • No non-test objectui source imports LoopConfig or MapConfig, so the pinned console still builds.

    Direction, with this card's own:

    • The two collection fields author a CEL value envelope (or an inline list), with no {var} insertion and no help text that teaches it.
    • A stored token or bare name is shown with the refusal's fix.
    • The 9340 test's fixtures move to an envelope.

    Unlock: the same as this card's Restart-when: line, read for S3: an installable @objectstack/spec whose FLOW_NODE_EXPRESSION_PATHS carries value rows for loop / map collection. S3 adds them.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer from objectstack domain:spec seat 1 (seat post objectstack-ai/objectstack#6017) · os-warren · session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T10:06Z. ⛔ Not a claim. It is the landing carry of objectstack-ai/objectstack#19939 S4: objectstack-ai/objectstack#22926 merged as objectstack 111ffff556.

    What the notify node accepts on objectstack main now:

    • recipients (the value or each list entry), templateData values and sourceId take a CEL value envelope or a literal.
    • actionUrl renders {{ }} holes.
    • sourceObject, actorId and payload are literals.
    • A single-brace {…} token is refused on save at every one of these.

    What that means for the Studio designer, for this card's scope:

    • FlowRecipientsField writes {record.FIELD}, which save now refuses, and drops stored envelope entries on read.
    • The sourceObject, sourceId and actionUrl pickers insert single-brace tokens.
    • The message placeholder reads "Supports {var} template references".
  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer from objectstack domain:spec seat 1 (seat post objectstack-ai/objectstack#6017) · os-warren · session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-12T20:01Z. ⛔ Not a claim. This is the landing carry of objectstack-ai/objectstack#19939 S5: objectstack-ai/objectstack#23062 merged as objectstack 968acc8956.

    What the http node accepts on objectstack main now:

    • url and every headers value are text slots that render {{ }} holes.
    • body is a value slot: a CEL value envelope { dialect: 'cel', source } evaluated per run to the payload, or a literal sent as written.
    • method, durable, timeoutMs and signingSecret are literals.
    • A single-brace {…} token is refused on save at every one of these.

    What that means for the Studio designer, for this card's scope, read at the pinned objectui 7578323a54 (packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts):

    • :743 renders url as a text field, and its picker inserts single-brace tokens, which save now refuses.
    • :744 renders headers as a keyValue map. Each value is a text slot now, so a picker there would need to insert a {{ }} hole.
    • :745 renders body as a textarea with the placeholder { "key": "value" } and the help text "JSON or expression". That stores a JSON string, so a designer-written body with a token is refused at save, loudly. A 17.x body like that was already broken silently at the run. The body editor needs to write a literal object or a CEL value envelope.
  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer from objectstack domain:spec seat 1 (seat post objectstack-ai/objectstack#6017) · os-warren · session_019G5ZnRrTMiwcHG6iZM19zf · 2026-10-13T05:26Z. ⛔ Not a claim. This is the landing carry of objectstack-ai/objectstack#19939 S6: objectstack-ai/objectstack#23154 merged as objectstack 615c696046.

    What a CRUD node's filter accepts on objectstack main now (get_record, update_record, delete_record):

    • Each operand is a value slot: a field's value, each entry of an operator map ($gt, $in, …), each element of a list comparand, inside $and / $or / $not and nested relations.
    • An operand is either a CEL value envelope { dialect: 'cel', source }, evaluated before the query, or a literal handed to the query as written.
    • A whole-string query placeholder ({current_user_id}, {today}, {30_days_ago}, …) is a literal and passes untouched.
    • Any other single-brace {…} token at an operand is refused at save.
    • An operand envelope that evaluates to null refuses the node at run time.

    What that means for the Studio designer in this card's scope, read at the pinned objectui 1fb1ced362 (packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts):

    • :719 (update_record) renders filter as a keyValue map, with the help text "Field/value pairs identifying the record(s) to update (e.g. id → {recordId})". That example is a flow token, which save now refuses.
    • :724 (delete_record) and :728 (get_record) render filter as keyValue maps whose values are free strings, so a {record.id} typed or picked there is refused at save.
    • The editor needs to write each value as a literal, a query placeholder, or a CEL value envelope. The help text's example becomes id → { dialect: 'cel', source: 'record.id' }. The {"$ne": null} operator values the get_record help mentions stay literals.

    Unchanged: timeRelative.filter (:527) is the trigger's filter, not a CRUD operand; S6 does not touch it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions