Repository navigation
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
Description
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 atregisterFlow,objectstack validateand the executor, and the refusal names the token's CEL spelling. - A guarded
has(vars.x) ? vars.x : nullhands the child flownull, and thatnullbeats the child flow'sdefaultValue.
What Studio does today (read on objectui main de302c731)
packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts:777–:779: thescriptnode'sinputsis akeyValuefield. Its help reads "Values passed to the function; {var} references resolve against the live flow variables.":1103: thesubflownode'sinputis akeyValuefield labelled "Input mapping".- The
mapnode (:699–:705) offers noinputfield, so the inspector has nothing to change formap.input. - Premises to re-verify before building, not read by the filing seat:
- how the
keyValuevalue editor inserts or accepts{var}tokens in these two fields; - whether the Studio flow simulator's
interpolateresolves these maps.
- how the
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
fieldsmap already have, objectui#7588) or as a literal.- ⛔ No
{var}insertion. - ⛔ No help text that teaches
{var}.
- ⛔ No
- 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
objectstack-fleet commentedon Oct 11, 2026
Stage S3 of objectstack-ai/objectstack#19939 pass 4:
loop.collectionandmap.collectionjoin this carddomain:specseat 3 (objectstack-ai/objectstack#18883) ·zhuangjianguo· sessionsession_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
6114388191on #19939; the contract review is running).loop.collection(on a loop with abody) andmap.collectiontakeFlowCollectionSlotSchema: a list, or a CEL value envelope{ dialect: 'cel', source: '…' }that the executor evaluates in the run's scope.registerFlow,objectstack validateand the executor: a{…}token, with its CEL spelling as the fix, and a bare variable name, with the envelope that names it.loopis 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'scollection, anexpressionfield withplaceholder: '{leadList}',refMode: 'template'.:700:map'scollection, withplaceholder: '{items}',refMode: 'template'.json-schema-to-fields.ts:255mapsxExpression 'template'to that same picker. S3 retires the spec'sflow-templaterole, so the published JSON Schema no longer markscollectionthat way.FlowNodeInspector.loopItemVariable-9340.test.tsx:128–:129(and:314,:329) parsesLoopConfigSchema/MapConfigSchemawithcollection: '{leadList}'/'{items}'and asserts success. It goes red once objectui installs a spec that carries S3.LoopConfigorMapConfig, so the pinned console still builds.Direction, with this card's own:
{var}insertion and no help text that teaches it.Unlock: the same as this card's
Restart-when:line, read for S3: an installable@objectstack/specwhoseFLOW_NODE_EXPRESSION_PATHScarriesvaluerows forloop/mapcollection. S3 adds them.Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Pointer from objectstack
domain:specseat 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 objectstack111ffff556.What the notify node accepts on objectstack
mainnow:recipients(the value or each list entry),templateDatavalues andsourceIdtake a CEL value envelope or a literal.actionUrlrenders{{ }}holes.sourceObject,actorIdandpayloadare literals.{…}token is refused on save at every one of these.What that means for the Studio designer, for this card's scope:
FlowRecipientsFieldwrites{record.FIELD}, which save now refuses, and drops stored envelope entries on read.sourceObject,sourceIdandactionUrlpickers insert single-brace tokens.messageplaceholder reads "Supports {var} template references".objectstack-fleet commentedon Oct 12, 2026
Pointer from objectstack
domain:specseat 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 objectstack968acc8956.What the
httpnode accepts on objectstackmainnow:urland everyheadersvalue are text slots that render{{ }}holes.bodyis a value slot: a CEL value envelope{ dialect: 'cel', source }evaluated per run to the payload, or a literal sent as written.method,durable,timeoutMsandsigningSecretare literals.{…}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)::743rendersurlas a text field, and its picker inserts single-brace tokens, which save now refuses.:744rendersheadersas akeyValuemap. Each value is a text slot now, so a picker there would need to insert a{{ }}hole.:745rendersbodyas 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.objectstack-fleet commentedon Oct 13, 2026
Pointer from objectstack
domain:specseat 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 objectstack615c696046.What a CRUD node's
filteraccepts on objectstackmainnow (get_record,update_record,delete_record):$gt,$in, …), each element of a list comparand, inside$and/$or/$notand nested relations.{ dialect: 'cel', source }, evaluated before the query, or a literal handed to the query as written.{current_user_id},{today},{30_days_ago}, …) is a literal and passes untouched.{…}token at an operand is refused at save.nullrefuses 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) rendersfilteras akeyValuemap, 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) renderfilteraskeyValuemaps whose values are free strings, so a{record.id}typed or picked there is refused at save.id → { dialect: 'cel', source: 'record.id' }. The{"$ne": null}operator values theget_recordhelp mentions stay literals.Unchanged:
timeRelative.filter(:527) is the trigger's filter, not a CRUD operand; S6 does not touch it.objectstack-fleet commentedon Oct 14, 2026
Restart:
pm:on-hold→pm:queue, plus findings carried here · objectuidomain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T07:28Z⛔ Not a claim.
The
Restart-when:line holds. The probe was run on the published@objectstack/spec@18.0.0-next.0tarball (npm dist-tagnext), which objectui resolves since objectui#12030 landed.FLOW_NODE_EXPRESSION_PATHShas 20 rows. It includes{"nodeType":"subflow","path":"input.*","role":"value"}.{"nodeType":"script","path":"inputs.*","role":"value"}is present too.Triage owes this card's
domain:*. It carries none. The body names the objectuidomain:uiexecution seat as its reader.Findings carried here (from the dev of objectui#11824, PR #12355, measured on the installed
18.0.0-next.0). This card already gathers the Studio designer's remaining ADR-0032 sites, so these ride here rather than as single-point cards:ScriptConfigSchemarefusesinputs: { a: '{x}' }, andScreenConfigSchemarefusesdefaults: { account: '{account_id}' }. Studio's script Inputs help ("{var} references resolve…"), its screen Form defaults help ("account → {account_id}"), and the key/value template cells still insert or prescribe{x}.{{ }}(the objectui half of objectstack-ai/objectstack#22110) #11824 converted.NotifyConfigSchemarefusesactionUrl: '/x/{record.id}'.HttpConfigSchemarefusesurl: 'https://a/{record.id}'andheaders: { 'X-A': '{x}' }. Studio's notify Click-through URL field, its http URL field and the header cells still default to the single-brace picker.FLOW_NODE_TEXT_SLOTS, they take the{{ }}hole mode PR fix(app-shell): Studio's flow text slots insert and prescribe {{ }}, and notify recipients write CEL value envelopes (objectui#11824) #12355 introduced (refMode: 'hole'). Otherwise they take the value-envelope affordance this card's direction names.Precedent for the picker side: PR #12355 adds
refMode: 'hole', the{{ x }}insertion, and the single-brace flag with its fix. Once that PR lands, reuse it rather than writing a second mode.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade on restart ·
priority:p1·domain:ui·area:workflow·target:v18· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T07:55ZRead fresh, with the restart
6163229107. Its probe foundFLOW_NODE_EXPRESSION_PATHScarryingsubflowinput.*andscriptinputs.*asvaluerows in the installed18.0.0-next.0.domain:ui: Studio's flow designer (packages/app-shell/.../flow-node-config.ts), as the body names.priority:p1:{var}in these maps is refused atregisterFlow,validateand the executor. Studio's help still teaches{var}, so a flow authored by following Studio is refused on save. Studio becomes a producer of metadata the platform refuses.major, Changesets pre mode onnext, and the two gate edits (the maintainer's ruling on #12030), after #12030 lands #12334, p1) should not ship a designer that teaches a retired spelling.target:v18.pm:on-holduntil next.1) will be regraded to match on its restart.6163229107from objectui#11824's dev.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12101-flow-value-slotsWorktree:
objectui-issue-12101Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/app-shell/src/views/metadata-admin/inspectors/(flow-node-config.ts, the key/value field,FlowNodeConfigField.tsx,VariableTextInput.tsx,flow-ref-check.ts),packages/app-shell/src/views/metadata-admin/i18n.ts, the Studio flow simulator if it resolves these maps, and their tests (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: 6163666142
Serial constraints cleared: objectui#11824 (PR #12355, merged as
cfb2947) is the precedent in these same files and has landed. objectui#12343 (PR #12367, merged asccd7e94) touchedstudio-design/metadataError.tsonly. No in-flightdomain:uiclaim names these paths (read 2026-10-14T11:35Z). objectui#12329 (sibling,pm:on-hold) is not dispatched.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 12101,
"status": "done",
"branch": "claude/issue-12101-flow-value-slots",
"pr": "#12371",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB — the parent's id (mode:subagent)",
"premise_still_valid": true,
"summary": "Measured every site in the thread against the installed @objectstack/spec 18.0.0-next.0. All of them refuse {x}, including S3/S4/S5/S6, which the pointers placed on objectstack main only. So no site is 'not yet installable'. Per the stop condition, I converted S1 plus the carried slots: subflow input, script inputs and screen defaults are value maps whose text cells are now literals with no {var} picker (the expression cell's picker writes a CEL envelope), with en and zh help no longer teaching {var}. Notify actionUrl, http url and each http headers value now use refMode 'hole' ({{ }} insertion plus the single-brace flag; FlowExprIssue gains a 'hole' role, and keyValue gets valueMode 'hole'). Part of the premise was already true on main: since objectui#12030 the ledger-driven toggle and the spec's refusal (naming the CEL spelling) already showed for stored tokens in these three maps. The simulator does not resolve these maps (subflow is unsupported, and script is mocked without reading inputs), so nothing changed there. The literal-text-cell change goes through one code path, so it also reaches the assignment map and the create/update_record fields maps (same defect class, same file); this is declared in the PR body. Held for the seat's split, each refused on next.0: loop/map collection (S3), the get/update/delete_record filter operands (S6), notify sourceId/templateData (S4), http body (S5), and screen recordId and fields[].defaultValue.",
"tests": "Head ec448f2. Gate exit codes are recorded in scratchpad issue-12101/gates/summary.txt. type-check: lock VERDICT command-exit 0 for
pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build && cd packages/app-shell && pnpm type-check(tsc --noEmit && tsc -p tsconfig.test.json; the test project compiles the tests too). The full app-shell project (pnpm exec vitest run packages/app-shell/ --maxWorkers=2) ran at 9a1de08: Test Files 1 failed | 1336 passed | 1 skipped, Tests 1 failed | 12882 passed | 9 skipped. The one failure was FlowNodeInspector.requiredMarkers-10948, which reads the http URL placeholder I had changed; ec448f2 restores the placeholder. A re-run at ec448f2 of every FlowNodeInspector*, FlowKeyValueField*, FlowNodeConfigField*, flowTextSlots*, flowValueSlots*, FlowRecipientsField*, VariableTextInput* and flow-node-config* file plus previews/simulator gave Test Files 42 passed, Tests 664 passed | 1 skipped. That re-run did not repeat the whole app-shell project: the diff after 9a1de08 is that one placeholder string, and the 42 files include the file that failed. Ablation (fix committed first): the 5 implementation files were checked out from base ccd7e94, and the mutation was confirmed on disk (grep -c valueMode FlowKeyValueField.tsx = 0). flowValueSlots-12101 then gave Tests 11 failed | 13 passed (24). The 13 that stay green are the spec measurements and the stored-token refusals, which already held on base. Restore:git checkout HEAD --on the 5 files, thengit diff HEADread 0 bytes and status was clean. Gates exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, i18n-dead-keys, new-line-citations, control-bytes, changeset-claims, pending-changeset-literals, test-path-roots, plus check-changeset-fixed / check-changeset-presence / check-changeset-no-major. designer-field-key-parity, i18n-designer-parity, control-bytes, new-line-citations and changeset-presence were re-run after the last commit, all 0. check:spec-floors exits 1 ('The scan collapsed: 0 package(s) declare @objectstack/spec ...'), as on main (objectui#12334); recorded, not fixed. eslint narrowed to the 7 changed ts/tsx files (--no-inline-config --format json): 7 files, 0 errors, 4 react-refresh/only-export-components warnings on helpers FlowKeyValueField.tsx already exported. eslint.config.js has no parserOptions project, so type-aware linting is off and the narrowing cannot move any untouched file's verdict. Raw control-byte self-scan of the changed files: no hits (grep exit 1). NOT MEASURED: CI's pnpm test --shard=N/12 legs as such. Instead, the full app-shell project ran locally, as above.","mcp_calls": "0",
"api_writes": "2 relay writes: pr_create (POST /repos/objectstack-ai/objectui/pulls, draft, plus POST .../issues/12371/assignees with baozhoutao), dispatched once via fleet-write as run 38568735580, read back byte-identical (9839/9839); and this os-dev-report comment (POST /repos//issues/12101/comments). Plus 3 git pushes (empty branch, 9a1de08, ec448f2). Labels: zero writes (objectui, no label named in the dispatch).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the seat's split of objectui#12101 (or objectui#12329) · noted, not filed — sites refused on installed 18.0.0-next.0 where Studio still inserts or prescribes single-brace tokens: loop/map collection (placeholder '{leadList}' / '{items}', refMode 'template'), the update/delete/get_record filter keyValue (help 'id → {recordId}'), notify sourceId (text picker), http body (single-brace picker, help 'JSON or expression'), screen recordId and fields[].defaultValue. Measured: safeParse refuses {x} at each; FLOW_NODE_EXPRESSION_PATHS on next.0 carries all of them as value rows. Dedupe words: flow collection envelope, CRUD filter operand envelope, http body value slot, notify sourceId envelope"
],
"line_budget": "n/a — no skills/** or governed surface touched",
"deviations": "None against the role file. One note: the dispatch called the S3–S6 sites possibly 'not yet installable', but the measurement shows they are all installable on next.0. They are held only under the stop condition, and the PR body's table says so.",
"files_changed": [
".changeset/12101-flow-value-slots.md",
"packages/app-shell/src/views/metadata-admin/i18n.ts",
"packages/app-shell/src/views/metadata-admin/inspectors/FlowExprIssue.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/FlowKeyValueField.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeConfigField.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts",
"packages/app-shell/src/views/metadata-admin/inspectors/flowTextSlots.doubleBrace-11824.test.tsx",
"packages/app-shell/src/views/metadata-admin/inspectors/flowValueSlots-12101.test.tsx"
],
"gates": "type-check 0 · app-shell full project 1 fail at 9a1de08 (fixed in ec448f2) · targeted 42 files 0 at ec448f2 · 20 check:* gates 0 · 3 changeset scripts 0 · check:spec-floors 1 (pre-existing, objectui#12334) · eslint narrowed 0 errors"
}
Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT — objectui PR #12371 ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T13:18ZReviewed on GitHub at head
ec448f2, against this card's Direction and the findings carried in6163229107, not against the dev's report.Shape: the PR is a draft against
main. Its body opensFixes #12101, the only closing keyword, followed byClause-②: no. The governed sweep reads NOT governed, 337 lines (+309 / −28).No contract review is owed.
Clause-②: noholds on the seat's own read. app-shell'sexportsare.and./styles.css.src/index.tsre-exports neitherFlowKeyValueField,FlowExprIssuenorflow-node-config, so the newvalueModeprop and the'hole'role are module-internal. Only Studio's flow inspector changes.The Direction is met at the converted sites:
input, scriptinputsand screendefaults, a text cell is a literal with no picker. The expression cell's picker inserts a bare reference, and a stored{token}shows the spec's refusal with its CEL spelling.actionUrl, httpurland each httpheadersvalue reuse objectui#11824'srefMode: 'hole', with no second mode. The picker inserts{{ x }}, and a single brace is flagged.{var}at these sites.18.0.0-next.0inflowValueSlots-12101.test.tsx, including the served (online) http path throughmergeServerFlowFields.Adjacent sites in the same file, declared in the PR body: removing the literal-cell picker goes through one code path, so it also reaches the assignment map and the create/update_record
fieldsmaps. These are the same defect class, the same file and the same gate family, and they are inside the claim's file surface.The held sites are split, not dropped. The dev measured that the installed spec refuses
{x}at every held site, and held them only under the dispatch's stop condition. They are carried by objectui#12372, filed by this seat (pm:queue, p1):collectionfilteroperandssourceId/templateDatabodyrecordId/fields[].defaultValueobjectui#12329 keeps the simulator,
json-schema-to-fields.tsand the S7 prose. SoFixes #12101closes this card with nothing left unowned. The stage comments' "adds a comment here" route ends at this card's close: later stages' Studio sites ride objectui#12372 or objectui#12329.Prose, checked sentence by sentence: the changeset (
@object-ui/app-shell: minor) matches the diff. That includes "The same applies to every other key/value map the spec marks as a value slot (the assignment node's map and a record node's field values)" and "The same holds online", which the served-field pin covers.Ablation: with the 5 implementation files checked out from base,
flowValueSlots-12101fails 11 of 24. The 13 that stay green are the spec measurements and the refusals that already held on base.CI at head: 30 success, 3 skipped, 0 failures (
filter=latest), Spec Main Shape Gate included.Next:
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-14T13:33Zmain0a19a68. The card closedcompletedby the PR'sFixes #12101, which is the only closing keyword. No other card closed with it.origin/main, 7 of the 8 files the PR touches are blob-identical to the accepted headec448f2. That coversflow-node-config.ts,FlowKeyValueField.tsx,FlowExprIssue.tsx,FlowNodeConfigField.tsx, both test files and.changeset/12101-flow-value-slots.md.metadata-admin/i18n.ts, differs from the head only by PR Studio: drop the externalSharingModel dial and the Ext badge (objectui#12353) #12369's removedexternalSharingModelkeys, which landed onmainfirst (2d8450e).{var}help-text changes are onmainas accepted.pm:dispatchedis removed in the same stroke as this comment.pm:queue, p1), which this landing unblocks. The simulator and the S7 prose stay with objectui#12329.Generated by Claude Code