Repository navigation
feat(spec)!: reserve the retired callout node types http_request, http_call and webhook; flow-node-http-callout-rename leaves the load path (F5-http of #23543) - #23954
Conversation
…ttp-callout-rename from the load path
…only probes Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
…shrink the test-typecheck ledger Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
…-http-reserved-node-names
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 142 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1faf7c8c827dbca0098ca0b8c2f45f450e648acf && git checkout 1faf7c8c827dbca0098ca0b8c2f45f450e648acf
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6d1660dc3b163c30c3a02d34de5cfc219940d958 f459d50c5b3869cc77fa6bdedced8ad732344eb6 && git checkout -B drift-repro 6d1660dc3b163c30c3a02d34de5cfc219940d958 && git merge --no-ff f459d50c5b3869cc77fa6bdedced8ad732344eb6
node scripts/docs-audit/affected-docs.mjs --json 6d1660dc3b163c30c3a02d34de5cfc219940d958
|
Contract reviewServed-tier: Inputs: card #23543 (body and all 36 comments, among them ruling 6182001882 items 6 and 7, claim 6214417833 and dev report 6217001433), rulings 6212082860 (#23775) and 6216220018 (#23903) with PR #23902's body, this PR's body, file list and net diff against its merge-base Gates on this head (the check-runs' conclusions are the verdicts). 41 check-runs, all completed. Two of the five required contexts are red. ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: FAIL Generated by Claude Code |
…-http-reserved-node-names
…be preload; no house-sentence marker in the D3 reason; the save door refuses a new flow (main's #23902), so the changeset says so Claude-Session: https://claude.ai/code/session_01SgC7iCUX8y2TeJAvz8wcaw Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Round 2, on the patched head, after the FAIL record 6217909163 on Gates on this head (the check-runs' conclusions are the verdicts). 48 check-runs, all completed, none red. The five required contexts are green: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 · ACCEPT · PR #23954 @
|
Part of #23543
Clause-②: yes (narrowing)
This PR lands family F5-http of ruling
6182001882(item 6), the last family, as the code half. The ADR-0018 dated note is its own Tier H PR,claude/issue-23543-f5-http-adr-0018-note, branched frommain. #23543 stays open for that note; the seat closes it once both have landed.What
Ruling F5-http A: "
http_request,http_callandwebhookare reserved inside the open node namespace: the authoring doors refuse them with the rename tohttp; … the entry retires; …eventType: 'webhook'on a wait node is untouched."FLOW_RESERVED_NODE_TYPESandreservedFlowNodeTypeRefusal(packages/spec/src/automation/flow-reserved-node-types.ts, exported from@objectstack/spec/automation) are read by the flow parse, the engine's registration refusal and the conversion itself.type, with the prescription naminghttpand@objectstack/spec 18. A top-level node meets apatternper name onFlowNodeSchema.type, theDriverType(F1) shape, so the generated JSON Schema carries it too (json-schema/automation/FlowNode.jsonproperties.type.allOfholds the three patterns). A node inside aloop/parallel/try_catchregion meets oneFlowSchemasuperRefine arm overcollectFlowGraphs, which skips the top-level graph, so each site is reported exactly once. The open namespace is otherwise unchanged:http, a plugin type, a name that merely contains a reserved one (http_request_v2,my_webhook) and a wait node'swaitEventConfig.eventType: 'webhook'all parse (pinned).AutomationEngine.registerNodeExecutorthrows when the executor'stypeor its descriptor'stypeis reserved, and registers nothing;registerNodeAliasgoes through it. Measured before the change: nothing stopped a plugin registering an executor under one of the names, and the engine is the one executor registry (every builtin andplugin-approvalsregister through it). No node could ever reach such an executor, because an authored node is refused at parse and a stored one is rewritten tohttpfirst, so the reservation is only true with this door closed (dispatch hypothesis 1, axis ③).flow-node-http-callout-renameisretiredFromLoadPath: true,retiredAfter: '17.7.0'(F1 to F4's stamp, still the newest stable under the18.0.0-next.2label). Its docblock's "stays on the load path" verdict is rewritten as a retirement record. Fixture unchanged.reservedNodeTypescan never contain one, so the guard had no case left. The rewrite is now unconditional. The generic ADR-0078 conflict channel (ConversionContext.reservedNodeTypes/reportConflict,onConflict, the save door's 409) is left in place; see Acceptance notes.flow-node-http-callout-types-reserved(noconversionIds: protocol 11 is belowMIGRATION_SUPPORT_FLOOR; norelevantWhen, so always listed, the F5-jsx shape), the removal-versionJOINrow (three refusals, one per name), the verdict-list pin (the kept list is now empty and the entry is listed among the retired), and a BREAKINGminorchangeset for@objectstack/specand@objectstack/service-automationwith FROM → TO, markedregistered flow-node-http-callout-types-reserved. NoRETIRED_KEYS_BY_MAJORentry (a value retirement, no key) and noSTEP18_RATIONALEfragment (no step-18 conversion), both the F5-jsx precedent. Noos migrate metasentence in the prescription: the removal-version pin's rule would demand--from 10, which the CLI refuses.The D3 text, tested against the tool
On a temp project with one flow carrying all three names, at this branch's build:
os validate,os buildandos lintexit 1 / 2 / 1, each printing the three refusals atflows.0.nodes.1.type/.2.type/.3.type(the config callsdefineStack).os migrate meta --from 17exits 0, applies no edit, prints the three refusals under "Migrated stack does not yet pass schema validation — 3 refusals left after the chain", and lists the D3 entry.--writesays "the chain made no mechanical change here, so no file was written" and the source is byte-identical afterwards (cmp).The entry's
reasonsays exactly that, plus the save door's 422 for a new flow (measured below). It spells the toolobjectstack migrate metawithout a--fromvalue, soretired-key-migrate-sentence.test.ts(which judges every backtickedos migrate meta --from Nunderpackages/spec/srcagainst the house sentence) has nothing to judge there.Stored rows (hypothesis 5)
A stored flow with an old node type still loads as
http, pinned at three seams:stored.test.ts(applyConversionsToStoredItem('flow', …)rewrites all three, unconditionally, a caller'sreservedNodeTypesnamingwebhookincluded),flow-load-conversion.test.ts(engine rehydration,registerFlow), andnested-region-parity.test.ts(awebhooknode in a loop body still runs ashttp, unchanged). metadata-core's artifact door still replays the retired rename inside its window (new case).The suites that drove this entry as a live subject (hypothesis 4)
After this PR no real entry is live on the load path (measured:
ALL_CONVERSIONS.filter(c => !c.retiredFromLoadPath)was exactly this one entry onmain). Decided per suite; no suite deleted, no assertion weakened.specconversions.test.tsnormalizeStackInputsink casesincludeRetired), as the CRUD and F3 cases already do. The guard pair now pins the unconditional rewrite. The live-window case and the three sink cases need a live entry, so they moved, assertion for assertion, to the newapply-live-window.test.ts, which appends a test-only probe withvi.mock(F2's approach). In their place: adefineStackrefusal pin and an authoring-funnel-vs-stored-door pin for the retired name.specregion-walk.test.tswebhookincludeRetired: the walker's subject is regions, and the stored seams are where a region-nested old node still has to come out canonical.specstack.test.tsdefineStack's conversion-notice sink and warn-oncestack-conversion-notices.test.tswith avi.mockprobe, sostack.test.tsdoes not mock the registry for its other 1,400 lines. Its two type errors moved with it (cast at the fixture, not ledgered); thetest-typecheck-debt.jsonrow forstack.test.tsshrank by those two.specstack-conversions-record.test.tsvi.mockprobe beside the page probe F4 added.metadata-coreartifact-forward-conversion.test.tsALL_CONVERSIONSfrom@objectstack/spec, the instance the door reads, and removed infinally; a premise line asserts no real entry is live. The door itself is the real one. Plus a new case: the retired rename still fires at this door.clistack-conversion-record-door.test.ts,validate-json-strict-exit.e2e.test.ts,build-json-failure-conversions.e2e.test.ts,validate-json-failure-conversions.e2e.test.ts,lint-conversion-notices.e2e.test.ts--strict, failure exitsvi.mock's reach, so the child loads a test-only preload with--import(test/fixtures/live-conversion-probe.mjs, theunbuilt-spec-dist.hook.mjsprecedent). It appends the same kind of probe toALL_CONVERSIONSin both builds of@objectstack/specthe child can load (measured: the ESM and CJS tables are two arrays, and adefineStackconfig can load through either). Every real entry stays as published. The record door also gains one case without the probe:os validate --jsonrefuseshttp_requestby name and records no conversion.specscripts/build-schemas-check-mode.test.tssurfaceflow.node.typeas a leaf colliderThe seat's call on F4's open question 2 refused "a test-only entry into the published registry". None is added: every probe lives in a test file or a test fixture outside every package's
files[].The metadata save door (hypothesis 6)
Not touched by this PR (
protocol.tsis outside this family). PR #23902 (cf071687f1, ruling6212082860letter B) landed onmainand is merged into this branch: the save door no longer replays retired conversions on a NEW flow. Measured atf459d50cat the realPUT /api/v1/meta/flow/:namedoor (the real-stack harness ofpackages/rest/src/meta-flow-save-retired-spelling.test.ts, run as a throwaway file and deleted), saving a new flow whose one node is typed with each name:AutomationEngine)http_requestINVALID_METADATAatnodes.1.type, this family's prescription; nothing storedhttp_callwebhookhttp(control)httphttpThe changeset and the D3 entry name the save door among the refusing doors. A stored row that already carries an old name keeps the full chain (ruling
6212082860, pinned by PR #23902).Readers and examples (hypothesis 7)
No example, skill, hand-written doc or published fixture in this repository authors one of the three names as a node type (
git grepovercontent/,skills/,examples/,apps/). The one test fixture that listed one as a registry entry,runtime'shttp-dispatcher.test.tsGET /actionsmock, now listshttp. Two docblocks this change made false were corrected:stored.ts(why flows are not converted by the metadata-protocol seams) andengine.ts(registerNodeAliasand the rehydration comment).eventType: 'webhook'is untouched everywhere.objectui (hypothesis 8)
carrier:the seat's objectui card at landing. Read sites of the three names at objectuimainf78efdd1e3, identical in file set at the pindaeb77fc84: preview samplesapps/console/src/preview-samples.ts:237,244(twohttp_requestnodes); inspector aliaspackages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts:1307(http: 'http_request') and itshttp_requestconfig group:771; canvas casespreviews/flow-canvas-parts.tsx:88-89,261,265,326,330,424; simulatorpreviews/FlowSimulatorPanel.tsx:57andpreviews/simulator/flow-simulator.ts:86; pluscomponents/src/renderers/basic/metadata-viewer.tsx:271and the i18n keyengine.flowNode.http_request.label.FlowNodeSchema.type's TypeScript type is stillstring, so the pinned console build does not break on this change.Tests
Final head
f459d50c(two merges oforigin/main, both throughos-regen-merge.sh, no conflict; the second brought PR #23902): spec build +check:generated"All 14 generated artifacts are up to date";@objectstack/spectest(local project) 688 files / 20914 passed (1 todo);@objectstack/spectest:repo(the wholerepoproject) 64 files / 1082 passed;service-automationtypecheck 0 andflow-load-conversion+nested-region-parity+canonicalize-stored-flow72 / 72;metadata-protocolprotocol.save-flow-canonicalization34 / 34;restmeta-flow-save-retired-spelling6 / 6;clitypecheck 0 and the five conversion suites 64 / 64 (four e2e underOS_TEST_TIERS=nightly42, the record door 22); ESLint (--no-inline-config) over every.ts/.mjsfile of the diff exits 0. Earlier onbf004d7a:metadata-coretypecheck 0 and test 18 / 737;runtimetypecheck 0 andhttp-dispatcher.test.ts248;cliunit tier 302 files / 4500;service-automationtest 186 files / 2507.Gates on
f459d50c:dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 118, each run with its exit captured before any pipe;--ran: "118 derived famil(ies) accounted for — 115 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3)". All 115 exited 0, among themcheck-adr-0087-registration --base origin/main,check-changeset-no-major,check:spec-changes,check:upgrade-guide,check:docs,check:api-surface,check:authorable-surface,check:cross-package-test-inputs,check:doc-authoringand the threecheck:i18n*gates. The three NOT-MEASURED are prerequisite refusals declared to CI:check-plugin-teardown-shape --self-test(shallow clone),check:skill-examples(client-reactunbuilt) andcheck:dual-build-cjs-loads(a full build absent). Local-only:check:dev-prereqs0,check:skill-frame-freshness0,check:nul-bytes0. PR check-runs onf459d50c: 41, all completed, 36 success and 5 skipped, no failure.Ablation (
scripts/ablation-replace.mjs, restored to the HEAD blob each time,git diff HEADempty):retiredFromLoadPath/retiredAfterdeleted). Red, 6: the verdict-list pin; thedefineStackrefusal pin; the authoring-funnel-vs-stored-door pin; and three removal-version pins (theJOINrow, "every joined entry is a retired conversion", "the joined rows exercise the derivation in both directions"). Spec tests importsrc, so no dist leg.NODE_OPTIONS. Red, 2:lint-conversion-notices's two "the fixture REALLY converts" anti-vacuity cases.reasongiven back its first-round marker (os migrate meta --from 17in backticks). Red, 2:retired-key-migrate-sentence.test.ts("every prescription sentence in every corpus is house-form or MIXED two-clause" and its anti-vacuity floor), therepo-project test behind the first round'sTest Core (1/6)red.Acceptance notes
ConversionContext.reservedNodeTypes/reportConflict,ApplyConversionsOptions.onConflict, the engine'sconflictsarray and the save door'sFLOW_CONVERSION_CONFLICT409 stay as a generic mechanism, with zero entries using it. The seat retires it as its own card at landing (it touchesprotocol.tsandengine.ts), not in this PR.http_requeston a top-level node andwebhookinside a loop body answers both issues in one parse, atnodes.1.typeandnodes.2.config.body.nodes.0.type.Generated by Claude Code