Repository navigation
fix(service-automation)!: a connector_action call whose input breaks the action's declared inputSchema fails before dispatch, naming each parameter - #28035
Conversation
…he action's inputSchema before dispatch The connector_action executor now judges the evaluated input against the action's declared inputSchema (required, type with OpenAPI nullable, properties, items) after the value slots resolve and before the handler is called. A failing call fails the step with code VALIDATION_ERROR, names every missing or mistyped parameter by its dotted path, and sends nothing. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgX3dtTQwah2MjCjf5i
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgX3dtTQwah2MjCjf5i
📓 Docs Drift CheckThis PR changes 2 package(s): 20 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 12 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 cf7f389fc9d5f8b1b2b78474b4058327ed5327f1 && git checkout cf7f389fc9d5f8b1b2b78474b4058327ed5327f1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e1dde1a5c15566bcc24d6c641b53d8282fcf655b 615683feb3c9472259fdcc1dfefc73028d1b9d46 && git checkout -B drift-repro e1dde1a5c15566bcc24d6c641b53d8282fcf655b && git merge --no-ff 615683feb3c9472259fdcc1dfefc73028d1b9d46
node scripts/docs-audit/affected-docs.mjs --json e1dde1a5c15566bcc24d6c641b53d8282fcf655b
|
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Contract-review-claim: session_01L6bhgUCizoYWQVJWjhdR6F · head 7c7eed0 · 2026-10-30T09:50Z |
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Contract reviewServed-tier: Reviewed at 2026-10-30T10:06Z, adversarially, on: the net diff origin/main...pr28035 (base f296334, 3 files, +378/-0), card #27995 (body; triage 6517799549; claim 6518217530 and amendment 6518274130; os-dev-report 6519820229), the PR body, file list and comments (Contract-review-claim 6519856849), and the head's check-runs. Read-only: nothing built, run or re-run. ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: FAIL |
…prescribe the cast for text where a number is declared Contract review round 1 on the connector_action inputSchema check: null on an optional property no longer refuses (a provider that serializes the section drops it); a refusal of text where an integer, number or boolean is declared prescribes int()/double()/bool(); a missing required section also names the parameters it requires. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgX3dtTQwah2MjCjf5i
…t of an action's inputSchema The connector_action executor now refuses a call whose input breaks the action's inputSchema, so buildInputSchema stops demanding the Accept, Content-Type and Authorization header parameters (ignored per OAS 3.0.3) and the header or query parameter the connector's api-key auth fills. Pinned through a real boot on buildInputSchema's own output. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgX3dtTQwah2MjCjf5i
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Contract-review-claim: session_01L6bhgUCizoYWQVJWjhdR6F · head 615683f · 2026-10-30T11:23Z |
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F Contract reviewServed-tier: Delta review at 2026-10-30T11:36Z, adversarially, of head 615683f against the FAIL record 6520142501 (2026-10-30T10:07Z) on 7c7eed0. Read: the net diff origin/main...pr28035 (base now 847e424, 5 files, +602/-2) against the old net diff (3 files, +378/-0); the two non-merge commits since 7c7eed0 (b2149dc, 6785709); claim amendment 2 6520164540 (2026-10-30T10:08Z); os-dev-report 6521455883 (2026-10-30T11:22Z); the PR body, file list and comments (claim 6521477850 on this head); the head's check-runs. Read-only: nothing built, run or re-run; the one merge below is a server-free ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Implemented-by: VERDICT: PASS |
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F 维护者速读(席位终稿)
席位意见: ACCEPT。契约复核首轮 FAIL 指出的两条 must-fix 都已修好,增量复核 PASS;最终 head 上的 battery 全绿。 席位核对ACCEPT · head
|
domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F
Requested by Sales · project thread
domain:server#2 · os-sales · session_01L6bhgX3dtTQwah2MjCjf5i
Fixes #27995
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: #28041 —
skills/objectstack-automation/SKILL.md,skills/objectstack-automation/references/integration-recipes.mdandcontent/docs/automation/connectors.mdxstill describe the input as unchecked; filed for after this merges (contract review 6520142501). Sibling call site: the pull door, #28040.维护者速读
connector_action节点在把请求发给连接器之前,先用该动作声明的inputSchema校验求值后的输入;缺必填参数或类型不对时,这一步失败,错误码VALIDATION_ERROR,逐个点名参数路径并给出改法,请求不发出去。inputSchema只是摆设,OpenAPI 的getPet不传petId也照样发出去,实测在 main 上发出的是字面量/pets/%7BpetId%7D且这一步报成功。分诊裁定必须本地响亮拒绝。required、type(含 OpenAPInullable)、properties、items,$ref/oneOf/enum/format等一律不判,避免误拒。仓库内示例与 dogfood 的连接器调用都合规(见盘点)。回滚即还原connector-nodes.ts一个提交。packages/services/service-automation/src/builtin/connector-nodes.ts(派发前的校验块与文件尾的collectInputSchemaViolations)、src/builtin/connector-input-schema.test.ts、.changeset/27995-connector-action-input-schema.md。pnpm --filter @objectstack/service-automation exec vitest run --maxWorkers=2 src/builtin/connector-input-schema.test.ts,预期 14 个测试全绿;另有pnpm --filter @objectstack/connector-openapi exec vitest run src/input-schema-dispatch.test.ts,4 个测试全绿。Measured at the real door (LiteKernel + AutomationServicePlugin + ConnectorOpenApiPlugin)
A declared
provider: 'openapi'connector overGET /pets/{petId}(petIdpath parameter,required: true,type: integer), a flow whoseconnector_actioncallsgetPetwithinput: {}, a recordingfetchImpl. One-off probe, not committed.origin/main,connector-nodes.tsat main)success: truesuccess: false["https://petstore.example.com/pets/%7BpetId%7D"]: the literal placeholder was sent[]connector_action 'call': input for 'pets.getPet' does not match the action's inputSchema — missing required 'path'. Nothing was sent. Fix connectorConfig.input so it satisfies the inputSchema GET /api/v1/automation/connectors lists for this action.Inventory (asked for by the order)
1. The
inputSchemashapes the producers emit.connector-openapi(buildInputSchema):{ type: 'object', properties: { path, query, header, body }, required }. Each section is an object schema with its ownrequired;pathis required whenever present; the parameter schemas and the body schema come from the document as written, so they may carry OpenAPI 3.0nullable,format,example,enum, and an unresolved$refwhen the caller did not dereference the document.connector-mcp: the server'stools/listinputSchema, passed through verbatim: any JSON Schema draft (often$schema2020-12 or draft-07), commonly withadditionalProperties: false,$defs/$ref,enum, combinators.type,required,propertiesand per-propertytype(connector-slack:chat.postMessagerequireschannel,chat.updaterequireschannelandts,callrequiresmethod;connector-restrequest: norequired).connector-imapdeclares no connector actions.2. An existing JSON Schema validator to reuse. The only one in the repo is
ajv(+ajv-formats), used privately by@objectstack/objectql'sjson_schemavalidation rule. It is not reachable from here:objectqlis a devDependency ofservice-automation(tests only), so importing it at runtime is whatcheck:undeclared-dep-importsrefuses, and addingajvas a new runtime dependency is outside the claimed file surface (package.json+ lockfile). It would also be the wrong tool for these schemas: ajv compiles$refeagerly and throws on an unresolved one, rejects an unknown$schemadraft by default, and does not know OpenAPI 3.0nullable, so it would refuse calls the declared schema permits. No other JSON Schema checker exists inservice-automationor its runtime dependencies. So the check is a small, deliberate subset written inconnector-nodes.ts(collectInputSchemaViolations):required,type(type lists,nullable: true,integer= whole number), and descent throughpropertiesanditems. Everything else is not judged and never refuses. If the seat prefers full-draft validation through ajv, that is a dependency change outside this claim.3. Calls in the repo that this turns into failures. None found:
examples/app-showcase/src/automation/flows/index.ts, the only flows withconnectorConfig:slack.chat.postMessagesendschannel+text(strings,channelrequired ✓);rest.requestandshowcase_status_api.requestsendmethod+path(strings, nothing required ✓);showcase_mcp_tools.echo_uppersendstext: 'objectstack'against the fixture's{ text: string, required: ['text'] }✓.showcase_status_openapi.getHealthtakes no parameters, so it has noinputSchema.connector-mcp,connector-rest,connector-slack,connector-openapisuites green on this branch (below); the wholeservice-automationsuite green; the full dogfood battery green on the final head (below), includingshowcase-declarative-mcp.dogfood.test.ts.4. Out of scope, as ordered. No registration-time check: #27968 owns judging a constant
inputat registration through its ledger path. This change judges only at run time, on the evaluated input.What changes
connector-nodes.ts: afterresolveValueSlotMapevaluatesconnectorConfig.inputand before the handler is called, the evaluated input is judged against the action'sinputSchema, read fromengine.getConnectorDescriptors()(the same schemaGET /api/v1/automation/connectorsshows the author; it is projected from the stored def unchanged, so noengine.tsaccessor was added). A failing call returns{ success: false, code: 'VALIDATION_ERROR', error }: the error names each problem by dotted path (missing required 'path.petId','query.verbose' must be boolean, got string,'tags[1]' must be string, got number), at most five then a count, says nothing was sent, and points at the connectors route. The handler is never called.runtimefailure, not a guard refusal (refuseNode): the input can come from run data, so afaultedge ortry_catchcan handle it;$error.codeisVALIDATION_ERROR, an existingStandardErrorCode, so nopackages/specchange.$ref,oneOf/anyOf/allOf,enum,format, bounds,pattern,additionalProperties, anytypename outside the JSON Schema seven, and values with no JSON spelling (aDate, aMap). An action with noinputSchemadispatches whatever it is given.@objectstack/service-automationpatch, BREAKING accept-set narrowing, ADR-0087not-required (no-migration-prescription).Tests
src/builtin/connector-input-schema.test.ts(new, 11), throughAutomationEngine+installBuiltinNodeswith the three producers' schema shapes:input: {}namespath(and afaultedge receives$error.code === 'VALIDATION_ERROR');{ path: {} }namespath.petIdwith the fix; two missing are both named; a code connector'schannel.integer; a CEL envelope evaluating to the wrong type (judged on its evaluated value); an array item, by index.nullableadmitsnull.enum,format,$ref,oneOf, extra keys underadditionalProperties: false) never refuse; an action with noinputSchemadispatches an absent input as{}.Red before the fix (test committed with the fix; run on the tree before the executor change)
Ablation through scripts/ablation-replace.mjs (fix committed first)
The four over-refusal pins stay green under the ablation, as preservation pins should.
Verification
Final head
615683fe(patch round for contract review 6520142501; os-dev-report 6521455883): derived gates 60/60 exit 0,--ran0 NOT-MEASURED;service-automation224 files / 2860 tests;connector-openapi71,connector-mcp56,connector-rest27,connector-slack10 green; full dogfood battery 357 files passed / 3 skipped, 2875 tests passed / 20 skipped, 0 failed. Contract review delta 6521700736 PASS on this head.The readings below are from the first head 7c7eed0, kept for the record.
@objectstack/service-automation:tsc --noEmit+check:test-typecheckgreen (a9f9f28); vitest 224 files / 2852 passed + 1 expected fail on 7c7eed0.connector-mcp56,connector-openapi67,connector-rest27,connector-slack10 tests green against the rebuiltservice-automation(a9f9f28).--no-inline-configon both changed.tsfiles: exit 0.dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 60 derived, 60 run, all exit 0;--ranreconcile60 run, 0 NOT-MEASURED(check-plugin-teardown-shape --self-testaftergit fetch --unshallow,check:dual-build-cjs-loadsafter building its seven missing dists).git merge-treeagainst fix(service-automation)!: a parent-id resume of a map whose item run is still suspended is refused, naming the item run #27951, feat(spec,metadata-protocol,rest,objectql,runtime): GET /meta/object serves switched-off packaged actions beside the item, and the disabled-action refusal reads as a plain sentence #27964, fix(mcp): an action on a sys_* object whose author exposed it to AI is listed and run over MCP, behind every gate #27940 clean.Full dogfood battery (narrowing arm), 7c7eed0
Acceptance notes
inputSchemais read throughgetConnectorDescriptors(), which builds the descriptor list per dispatch (one map over registered connectors). A dedicatedengine.resolveConnectorActionInputSchemabesideresolveConnectorActionEffectwould avoid that but is outside the claimed surface (engine.ts).Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgX3dtTQwah2MjCjf5i