Repository navigation
fix(app-shell): the flow inspector's fallback form writes only keys the node contract declares (objectui#11968) - #11979
Merged
objectstack-fleet[bot] merged 3 commits intoOct 8, 2026
Conversation
…he node contract declares (objectui#11968) The hand-written FLOW_NODE_CONFIG groups render whenever the engine has published no configSchema for a node type. Two of them wrote config keys their contract does not declare: - http_request offered an Output variable row writing config.outputVariable. The http executor never reads it, HttpConfigSchema is strict and refuses it by name, and registration refuses a flow carrying it, so a fallback edit saved a flow that never registered. The row is removed. - notify's Click-through URL row wrote config.url, the converted spelling of actionUrl. It now writes actionUrl. The new flow-node-config.contractKeys-11968 test judges every key every fallback group can introduce (field paths and objectList columns) against the contract of each spec node type the inspector resolves to that group, read from the installed @objectstack/spec: getBuiltinNodeConfigContracts(), SCHEMALESS_NODE_CONFIG_SCHEMAS, LEDGER_DECLARED_NODE_CONFIG_SCHEMAS, plus EndConfigSchema and ApprovalNodeConfigSchema; non-config paths are judged against FlowNodeSchema. Legacy render-only fields, the decision Target column and the two groups no contract judges (start, legacy_action) are exempt, each re-measured. It also reads a flow that already stores a removed key: shown in Advanced (JSON), kept through an unrelated edit, clearable there. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU
…-key fix moves (objectui#11968) Outside the claim's declared file surface, committed separately so the addition reads on its own: - FlowNodeInspector.requiredMarkers-10948: the http row listed the removed Output variable label among the unmarked ones, and the aria-required case used its placeholder as the optional sibling. The sibling is now the Body text control. - FlowNodeInspector.declaredUnmapped-11788: the "not brought back online" case read the offline notify `url` row, which now writes `actionUrl`. It reads a fixture schema that leaves out `topic` instead, with the full schema as its lit control. - metadata-admin i18n.ts: the zh overlay keyed the notify row `url`; it is re-keyed `actionUrl`, so the renamed row keeps its translation on both forms, and the http `outputVariable` overlay goes with the row. The pin file gains the zh reading for it. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU
…t" (objectui#11968) Wording only: a tombstone finding read "is a retired tombstone by its config contract". Every finding now says "in". Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgfduSkFrfM3eorB3UGfAU
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-11968-flow-fallback-keys
branch
October 8, 2026 15:36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11968
Clause-②: no — this removes one written key and renames another to the key the contract already declares; no published surface widens (triage's reading on the card).
What changes
Studio's flow-node inspector renders the hand-written
FLOW_NODE_CONFIGgroups (the fallback form) whenever the engine has published noconfigSchemafor a node type: whileGET /automation/actionsis loading, and when it answers non-ok or throws. Two groups wrote config keys their node's contract does not declare:config.outputVariable. Thehttpexecutor never reads it,HttpConfigSchemais strict and refuses it by name, and the engine's registration refuses a flow carrying it, so a fallback edit saved a flow that then never registered. The row is removed; the online form never offered it.config.url, the converted spelling ofactionUrl. It now writesactionUrl, the keyNotifyConfigSchemadeclares and the online form writes.A flow that already stores either key still opens: the key shows in the Advanced (JSON) block, an unrelated edit keeps it unchanged, and clearing it there leaves a config the contract accepts. Until the author clears it, registration refuses that flow exactly as it did before.
The pin
flow-node-config.contractKeys-11968.test.tsxholds every fallback group to its node's contract, read from the installed@objectstack/specat test time — no contract is hand-copied:objectListcolumn under the list element. Aconfigkey is judged against the config contract of every spec node type the inspector resolves to that group (fieldsForNodeTypeis alias-aware, sohttp_requestis judged as thehttpnode it renders for). Any other root (a sibling block, node-leveltimeoutMs) is judged againstFlowNodeSchema. A[REMOVED]tombstone counts as undeclared.getBuiltinNodeConfigContracts(),SCHEMALESS_NODE_CONFIG_SCHEMAS,LEDGER_DECLARED_NODE_CONFIG_SCHEMAS. Two associations are made by name because the spec exports no table for them, with the key sets still read from the spec:endtoEndConfigSchema, and the approval node type toApprovalNodeConfigSchema.__legacy__controller (render-only, they cannot introduce a key); the decisiontargetcolumn (virtual, stripped byapplyDecisionBranches); and the two groups no published contract judges,startandlegacy_action.Files outside the claim's original surface
Seat ruling A on my report (comment 6062596737), recorded in the seat's ACCEPT, extended claim 6060906675 by these three, as a bounded in-place amendment. They are in their own commit 8f32f89:
FlowNodeInspector.requiredMarkers-10948.test.tsx: its http row listed the removed Output variable label, and its aria-required case used that row's placeholder as the optional sibling. The sibling is now the Body text control.FlowNodeInspector.declaredUnmapped-11788.test.tsx: its "not brought back online" case read the offline notifyurlrow, which no longer exists. It reads a fixture schema that leaves outtopicinstead, with the full schema as its lit control.metadata-admin/i18n.ts,FLOW_FIELD_ZHhunks only: the notify overlay is re-keyedurltoactionUrlso the renamed row keeps its zh label on both forms, and the dead httpoutputVariableoverlay goes. objectui#11977 adds rows to this file in another region; whichever lands second mergesmain.Evidence, at head 1aaf06f
flow-node-configor namingFlowNodeInspector(57 files, the new pin included) pluspackages/core/src/utils/__tests__/column-identity.ratchet.test.ts, repo-rootpnpm exec vitest run, under the verify lock:Test Files 58 passed (58),Tests 904 passed | 1 skipped (905). Before the out-of-surface commit the same set read 2 files and 3 tests failed, the three cases named above.ablation-replace.mjsin wrap mode. A first attempt was a no-op: the replacement contained its anchor, the tool refused and restored, and no test ran. That reading is void. It was redone with another anchor:Tests 4 failed | 12 passed (16), and the main case printedhttp_request (node type 'http') writes config.outputVariable: 'outputVariable' is not declared by its config contract.url):Tests 5 failed | 11 passed (16), namingnotify (node type 'notify') writes config.url.git diff HEADempty.flow-node-config.tsis byte-identical to the head. The head commit then changed the findings' wording from "by" to "in".turbo run build --filter='@object-ui/app-shell^...':Tasks: 28 successful, 28 total.pnpm --filter @object-ui/app-shell type-checkexits 0; it runstsc --noEmitandtsc -p tsconfig.test.json, and--listFilesOnlylists all three touched test files.check:spec-symbols,check:metadata-write-doors,check:new-line-citations(0 new citations),check:control-bytes,check-changeset-presence.mjs,check-changeset-no-major.mjs,check:test-path-roots, the threecheck:vi-mock-*,check:changeset-claims,check:i18n-designer-parity,check:designer-field-key-parityandcheck:unreferenced-sources.eslinton the 5 touched ts/tsx files: 0 errors, 0 warnings.check:spec-floors: it needs a whole-workspace build and reported no-artifact for every package, so it judged nothing here.Acceptance notes
None of these was filed:
flow-scopenodeOutputRefsaddsconfig.outputVariablefor every node type, and its comment lists http and end among the binders. A stored http or end node carrying the undeclared key therefore offers a data-picker variable the engine never binds, and the flow simulator binds it the same way. It is a reader of a key that registration refuses, not a writer, and its reach was not measured. Carrier: none.FlowNodeInspector.endOutputVariable-9335.test.tsxhas a comment saying the spec declares the key on the http group. That was never true, and the row is now gone. Test comment only. Carrier: none.apps/console/src/preview-samples.ts, a dev-only sample, still carries an http node withoutputVariable. It does not write stored metadata. Carrier: none.HttpConfigSchemadeclaresconfig.timeoutMs, while both forms write node-leveltimeoutMs.mergeServerFlowFieldsdrops the served config field in favour of the node-level one. Which of the two the http executor reads was not measured. Carrier: none.Generated by Claude Code