You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
connectors: an offline OpenAPI normalization script for connector-openapi — Swagger 2.0 → 3.x, YAML → JSON, $ref bundled and dereferenced, absolute servers[0].url, sha256, and a generated include list #27373
carries item 4 of #26709; #26709 keeps items 1 and 3(c). Split by the triage seat's ruling on #26709 (comment 6487372008, 2026-10-29T03:48Z): "Item 4 is split into its own card, filed by the epic PM as a child of #27293. It brings a new third-party dependency (Swagger 2.0 → 3.x conversion and $ref bundling), so the PR is reviewed on that alone. Home: a script under packages/connectors/connector-openapi/, not a new os subcommand, unless the maintainer wants it published to authors. p2." Child of #27293 (E1). ⛔ Not a claim.
No converter exists in this repository: connector-openapi's dependencies are @objectstack/core and @objectstack/spec only; no YAML parser, $ref resolver or converter (re-check: git show origin/main:packages/connectors/connector-openapi/package.json).
"input an OpenAPI/Swagger URL or file; output a normalized 3.x JSON document (YAML→JSON, 2.0→3.0 conversion, bundle + dereference of $ref, servers[0].url made absolute), its sha256, and a generated include list from an allow-pattern. The shelf pipeline and app authors run it; the runtime never converts."
Home per triage: a script under packages/connectors/connector-openapi/ (not an os subcommand).
The new third-party dependency (conversion and bundling) is the PR's main review subject: name the package, its licence and its maintenance state in the PR body.
Acceptance
The Stripe (stripe/openapi, 3.0.0, MIT) and DocuSign (docusign/OpenAPI-Specifications, Swagger 2.0, MIT) documents normalize to 3.x JSON that the openapi provider loads with no $ref left.
The sha256 printed matches the bytes written.
A changeset for @objectstack/connector-openapi if the script ships in its package.
Business reason
#27293's E4 shelf (#27302, ruled A) packages vendor documents as connector apps; most vendor documents are Swagger 2.0, YAML or $ref-heavy, and the runtime refuses or mis-reads them by design. Without this script every author converts by hand and gets it wrong silently.
Reader
The epic PM of #27293 dispatches it to the domain:server lane's dev.
Dedupe
Split card by triage direction; parent #26709 holds the original item. The 500 most recent issues and PRs (all states, read 2026-10-29T04:10Z), titles grepped for normaliz|swagger|dereference: 2 hits, this card and #27210 (closed, a CLI census call-site defect, unrelated). #26709 itself is older than that window.
Claim: PM loop round 2
Session: session_017xRBcVBKAFk1ntXSjvPt7U
Account: sunhaolin (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-27373-openapi-normalize
Worktree: objectstack-issue-27373
Domain: domain:server
Seat: epic:#27293#1
File surface: packages/connectors/connector-openapi/scripts/** (new: the normalization script and its tests or fixtures), packages/connectors/connector-openapi/package.json (the new dependency and a script entry), pnpm-lock.yaml (regenerated by pnpm install, never by hand), packages/connectors/connector-openapi/README.md (how to run it), content/docs/automation/connectors.mdx (one pointer where the docs say "convert offline"), a changeset if anything shipped changes (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: opus (TIER_DEFAULT — dispatch-gates --tier this fire: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: none
Premise: holds (read at origin/mainb10fd655 2026-10-29T05:44Z: connector-openapi has no scripts/ directory and no YAML, $ref or conversion dependency; its files is dist, README.md, CHANGELOG.md, so a script under scripts/ does not ship on npm)
Serial constraints cleared: none — #26709 item 1 (PR #27387) merged 2026-10-29T05:42Z; none of the 60 most recent open PRs touches packages/connectors/connector-openapi/**; pnpm-lock.yaml is not a single-writer path (SINGLE_CLAIM_PATHS names only .objectui-sha), so a lockfile conflict is ordinary and the later lander regenerates it.
os-dev-report
{
"issue": 27373,
"status": "done",
"branch": "claude/issue-27373-openapi-normalize",
"pr": "#27436",
"session": "session_017xRBcVBKAFk1ntXSjvPt7U",
"premise_still_valid": true,
"summary": "Added packages/connectors/connector-openapi/scripts/normalize-openapi.mts, an unpublished repo script with a CLI (--in, --out, --include-pattern, --max-depth, --server-url, --empty-consumes). It reads a file or URL (JSON or YAML) and bundles external $refs with @scalar/json-magic. It converts Swagger 2.0 to OpenAPI 3.0 with @scalar/openapi-upgrader, inlines every $ref reachable from paths and webhooks, then drops the referenced-only components. It makes servers[0].url absolute and prints the sha256 of the bytes it writes, plus an include list built from the provider's own operationActionKey. Measured: Stripe cannot be fully inlined. Every one of its 612 operations expands past 2,000,000 JSON nodes even with only recursion cut, so a schema $ref at a recursion point or past --max-depth (default 2) becomes a typed stub whose description names the replaced reference. Real Stripe (3.0.0) and DocuSign (Swagger 2.0) outputs load through createOpenApiProviderFactory with 0 $ref (612 and 414 actions). DocuSign needs --empty-consumes application/json: 187 of its operations set consumes to an empty list, which would silently drop their request bodies, and the script refuses by default. The runtime (openapi-provider.ts, openapi-connector.ts) is untouched. No changeset: dist, README and CHANGELOG are unchanged, so the PR carries the skip-changeset label. The PR was opened without assignees: os-dev.md on main as merged into this branch (f9a4f40) now forbids pr_create assignees, which overrides the dispatch's assignees: [sunhaolin].",
"tests": "Final head f9a4f40: bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/connector-openapi test && pnpm --filter @objectstack/connector-openapi typecheck\" -> 'Test Files 5 passed (5) / Tests 63 passed (63)', typecheck = tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && check:test-typecheck OK, 'VERDICT command-exit 0'. The new file scripts/normalize-openapi.test.ts has 13 cases, all green (vitest run --maxWorkers=2 scripts/normalize-openapi.test.ts -> 'Tests 13 passed (13)'). Each normalized fixture output is loaded through the real createOpenApiProviderFactory, and no $ref is asserted on both the document and the materialized def. tsc -p tsconfig.scripts.json --listFiles includes both scripts/.mts and scripts/.test.ts. Ablation, run once and not kept: removing the inSchema && guard on SCHEMA_LITERAL_KEYS turned 2 of 13 tests red; scripts/ablation-replace.mjs showed anchor x1 -> x0 and blob 613cae347a0e -> 0cd0bc79a1fd, then restored the blob to the HEAD blob with git diff HEAD empty (the test imports source, so no build or dist leg applies). Real documents, run once locally: Stripe spec3.json -> 63,317,655 bytes, sha256 0ea3c37a0854ad2646a156404b1ef86cca57403855e3803cd4630601eb6fc7b2 (sha256sum equal), refsLeft 0, provider 612 actions, 0 $ref. Stripe by URL with --include-pattern '^GET /v1/customers' -> same sha256, include of 22, provider with that include -> 22 actions. DocuSign esignature.rest.swagger-v2.1.json -> exit 1 without --empty-consumes (187 operations named). With it -> openapi 3.0.4, 52,884,345 bytes, sha256 ceee523822c266fc1de6e5433915f6e4385753d2b239ce8b94f48cafe7d93d37 (sha256sum equal), refsLeft 0, provider 414 actions, 0 $ref, 192 actions with a body input. test:repo was not run locally because the diff does not touch it; Test Core is green in CI.",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (stderr: derived from the tree of objectstack-ai/objectstack at commit f9a4f40) printed 89 commands, the same list as at 252345b. Each was run with its exit code captured before any pipe. At f9a4f40: 88 exited 0. 1 exited 3 (PREREQUISITE NOT MET, nothing measured): node scripts/check-plugin-teardown-shape.mjs --self-test, because its pinned positive control is unreachable on a shallow clone; it grades the script, not this diff. --ran with the cmd :: exit N record: '89 derived famil(ies) accounted for — 88 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. The earlier run at 252345b had 86 at exit 0 and 3 at exit 3; skill-examples and dual-build-cjs-loads both went to exit 0 at f9a4f40. CI, on PR head f9a4f40 check-runs (71 total), every conclusion is success or skipped: Lint & Repo Gates success; TypeScript Type Check success (Type Check · workspace / source gates / consumer gates / debt ledger all success); Test Core success (1/8..8/8 success); Build Core success; Governed Surface Queue Guard success; Lint UI success; Type Check UI success; Test UI success (1/16..16/16 success); Build & E2E UI success; Live E2E UI success; Check Changeset success; Build Docs success; Check Documentation Links success; Dogfood Regression Gate success.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 — (1) fleet-write relay pr_create, one repository_dispatch (request fw-20261029T064429Z-3a7369, run 40831042346 success). It wrote POST /repos/objectstack-ai/objectstack/pulls (draft #27436; read-back: 14678 bytes sent, 14678 stored, identical) and POST /repos//issues/27436/labels (skip-changeset; read back with GET, still present at the end next to the size/xl, documentation, dependencies and tests labels set by the labeler). (2) post-stamped relay comment on #27373 (this os-dev-report). git push is not a REST write.",
"files_changed": [
"content/docs/automation/connectors.mdx (+19)",
"packages/connectors/connector-openapi/package.json (+4 -1)",
"packages/connectors/connector-openapi/scripts/normalize-openapi.mts (+615, new)",
"packages/connectors/connector-openapi/scripts/normalize-openapi.test.ts (+272, new)",
"packages/connectors/connector-openapi/scripts/fixtures/swagger2-esignature-shape.json (+103, new)",
"packages/connectors/connector-openapi/scripts/fixtures/openapi3-billing-shape.yaml (+139, new)",
"packages/connectors/connector-openapi/scripts/fixtures/billing-shared.yaml (+18, new)",
"packages/connectors/connector-openapi/tsconfig.scripts.json (+20, new; outside the claimed file surface)",
"pnpm-lock.yaml (+38, from pnpm install --lockfile-only)"
],
"line_budget": "n/a — no skills/** path touched; own diff +1228 / -1 across 9 files (under the 3000-line human-merge threshold)",
"deviations": [
"File surface: added packages/connectors/connector-openapi/tsconfig.scripts.json, a package-root config outside the claimed surface. Without it, scripts/** would be TypeScript that no tsc program reads (check:type-check-coverage SOURCES_COVERED/TESTS_COVERED). The typecheck script names it; precedent packages/spec/tsconfig.scripts.json.",
"README.md not edited, although the surface allowed it. README ships in the tarball, so editing it would need a changeset just for a pointer to a script the tarball does not carry. The how-to-run text lives in the script header and in content/docs/automation/connectors.mdx.",
"PR opened without assignees. The dispatch said assignees: [sunhaolin]. os-dev.md on origin/main, merged into the branch at f9a4f40, now reads 'PR ⛔ 不带 assignees', and fleet-write/ops.mjs says a dev's pr_create carries no assignees. Per the agent file, the file wins over the dispatch.",
"Commit trailers use the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS.md. The harness reminder's model-named Co-Authored-By was not used, because the pre-push hook refuses a model identifier in that pair.",
"pnpm-lock.yaml: the first pnpm add -D also flipped the jsdom peer from 30.1.1 to 29.1.1 in two unrelated snapshot lines. That lockfile was discarded and regenerated with pnpm install --lockfile-only from the HEAD lockfile, which adds only the 38 scalar lines. Never hand-edited.",
"Merged origin/main (4 commits, none touching this diff's paths) before opening the PR, then re-ran package test/typecheck and the full derived gate list at the new head f9a4f40.",
"Weekly download counts come from registry.npmjs.org/-/v1/search (json-magic 7,140,252; openapi-upgrader 5,508,440), because api.npmjs.org is refused by the egress proxy (CONNECT 403)."
],
"open_questions": [
{
"question": "Reachability: the card says the cloud shelf pipeline runs this script, but it is not in the npm tarball (files: dist, README.md, CHANGELOG.md) and has no bin, so only a checkout of objectstack-ai/objectstack can run it (pnpm install plus a build of the closure, because the script imports operationActionKey from src). I cannot read the cloud repository, so whether its pipeline can check out this repo is not measured.",
"options": [
"A: keep it a repo script (this PR); the shelf pipeline checks out objectstack and runs pnpm --filter @objectstack/connector-openapi normalize-openapi. Cost: a full workspace install per pipeline run; no published-surface change.",
"B: publish it as a bin of @objectstack/connector-openapi. This moves the two @scalar packages to dependencies (about 4 extra packages for every consumer of the connector) and adds a published CLI surface plus a changeset. Effort: small code change, needs a ruling (widens the published surface).",
"C: a new os subcommand in packages/cli. Triage ruled against it unless the maintainer wants it published to authors."
],
"recommendation": "A now, because it is what triage ruled and costs no published surface. Move to B only if the cloud pipeline cannot run a monorepo checkout, which the cloud side should measure."
},
{
"question": "The inlining cut is lossy by necessity, so its default needs a decision. Measured whole-document output size by --max-depth: depth 0 Stripe 6.1 MB / DocuSign 1.6 MB; 1 -> 14.8 / 4.9 MB; 2 -> 63.3 / 52.9 MB (shipped default); 3 -> 361 / 160 MB. At depth 2 a DocuSign envelopeDefinition body keeps recipients but stubs signer (a typed object leaf with an explanatory description).",
"options": [
"A: default 2 (shipped). Output is tens of MB for the whole document and much smaller with --include-pattern.",
"B: default 1. About 5x smaller, but request bodies lose one more named level.",
"C: separate budgets for inputs (parameters, request bodies) and outputs (responses), so action inputs stay deeper. More flags and code."
],
"recommendation": "A, because it keeps two named levels of every body and response at a size the provider loads in about 0.6 s. Revisit C if authors report missing body fields."
},
{
"question": "Swagger 2.0 operations with an empty consumes list (DocuSign: 187 of 414): the script refuses by default and fills them only with --empty-consumes MEDIA_TYPE. Should the default be to auto-fill with application/json instead?",
"options": [
"A: refuse by default (shipped). Loud, in line with 'prefer failing to falling back'; the shelf pipeline passes the flag explicitly.",
"B: auto-fill application/json and report the count. Friendlier, but it silently guesses a vendor's media type."
],
"recommendation": "A, because a guessed media type is the silent-wrong class this script exists to remove, and the refusal names the operations and the flag."
}
],
"out_of_scope_findings": [
"class: a · reach: public door = declarative provider: 'openapi' materialization (createOpenApiProviderFactory, boot path). Wrong answer: an OpenAPI 3.0.3 document whose path item carries parameters: [{name: 'petId', in: 'path', required: true}] materializes action getPet with inputSchema null (measured at f9a4f40 via a scratch tsx run). The required path parameter is missing from the action schema an AI or flow author reads; the handler still interpolates it if the caller happens to send it. · evidence: openapi-connector.ts collectOperations spreads only the operation object and buildInputSchema reads op.parameters, so path-item-level parameters are never read. · Seam: spec: OpenAPI 3.x Path Item Object parameters → runtime: packages/connectors/connector-openapi/src/openapi-connector.ts collectOperations/buildInputSchema · Swagger 2.0 input is unaffected after normalization, because the upgrader hoists path-level parameters into operations. This PR deliberately does not hoist them for 3.x input (no workaround of a generator defect). · dedupe words: path-level parameters, path item parameters, inputSchema missing path parameter, collectOperations, openapi generator parameters",
"carrier: none · noted, not filed: discriminator.mapping strings in a normalized document name component schemas that were dropped; dormant, the generator never reads discriminator.",
"carrier: none · noted, not filed: @scalar/openapi-upgrader lets the global produces win over an operation-level one (document.produces ?? operationItem.produces), an upstream third-party precedence slip with no effect on either real document."
]
}
ACCEPT → PR #27436 comment 6490424620 · head f9a4f408 · 2026-10-29T07:14Z. Ready and on auto-merge. Claim addendum (claim 6489063801, File surface): packages/connectors/connector-openapi/tsconfig.scripts.json, the type-check program for scripts/**; README.md deliberately not edited (it ships, the script does not). The dev's three open questions are decided on the PR (repo script; --max-depth 2; empty consumes refused by default). Follow-up filed: #27463 (path-item parameters never read by the generator).
MERGED → PR #27436 through the merge queue (merge commit 262ab67a4) · 2026-10-29T07:32Z. Closed completed by its Fixes #27373; pm:dispatched removed in this act, pm:epic kept. Follow-up: #27463 (path-item parameters never read by the generator).
Activity
objectstack-fleet commentedon Oct 29, 2026
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
Claim: PM loop round 2
Session:
session_017xRBcVBKAFk1ntXSjvPt7UAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-27373-openapi-normalizeWorktree:
objectstack-issue-27373Domain:
domain:serverSeat:
epic:#27293#1File surface:
packages/connectors/connector-openapi/scripts/**(new: the normalization script and its tests or fixtures),packages/connectors/connector-openapi/package.json(the new dependency and a script entry),pnpm-lock.yaml(regenerated bypnpm install, never by hand),packages/connectors/connector-openapi/README.md(how to run it),content/docs/automation/connectors.mdx(one pointer where the docs say "convert offline"), a changeset if anything shipped changes (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT —dispatch-gates --tierthis fire: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect cardThread-read: none
Premise: holds (read at
origin/mainb10fd6552026-10-29T05:44Z:connector-openapihas noscripts/directory and no YAML,$refor conversion dependency; itsfilesisdist,README.md,CHANGELOG.md, so a script underscripts/does not ship on npm)Serial constraints cleared:
none— #26709 item 1 (PR #27387) merged 2026-10-29T05:42Z; none of the 60 most recent open PRs touchespackages/connectors/connector-openapi/**;pnpm-lock.yamlis not a single-writer path (SINGLE_CLAIM_PATHSnames only.objectui-sha), so a lockfile conflict is ordinary and the later lander regenerates it.objectstack-fleet commentedon Oct 29, 2026
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
os-dev-report
{
"issue": 27373,
"status": "done",
"branch": "claude/issue-27373-openapi-normalize",
"pr": "#27436",
"session": "session_017xRBcVBKAFk1ntXSjvPt7U",
"premise_still_valid": true,
"summary": "Added packages/connectors/connector-openapi/scripts/normalize-openapi.mts, an unpublished repo script with a CLI (--in, --out, --include-pattern, --max-depth, --server-url, --empty-consumes). It reads a file or URL (JSON or YAML) and bundles external $refs with @scalar/json-magic. It converts Swagger 2.0 to OpenAPI 3.0 with @scalar/openapi-upgrader, inlines every $ref reachable from paths and webhooks, then drops the referenced-only components. It makes servers[0].url absolute and prints the sha256 of the bytes it writes, plus an include list built from the provider's own operationActionKey. Measured: Stripe cannot be fully inlined. Every one of its 612 operations expands past 2,000,000 JSON nodes even with only recursion cut, so a schema $ref at a recursion point or past --max-depth (default 2) becomes a typed stub whose description names the replaced reference. Real Stripe (3.0.0) and DocuSign (Swagger 2.0) outputs load through createOpenApiProviderFactory with 0 $ref (612 and 414 actions). DocuSign needs --empty-consumes application/json: 187 of its operations set consumes to an empty list, which would silently drop their request bodies, and the script refuses by default. The runtime (openapi-provider.ts, openapi-connector.ts) is untouched. No changeset: dist, README and CHANGELOG are unchanged, so the PR carries the skip-changeset label. The PR was opened without assignees: os-dev.md on main as merged into this branch (f9a4f40) now forbids pr_create assignees, which overrides the dispatch's assignees: [sunhaolin].",
"tests": "Final head f9a4f40:
bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/connector-openapi test && pnpm --filter @objectstack/connector-openapi typecheck\"-> 'Test Files 5 passed (5) / Tests 63 passed (63)', typecheck = tsc --noEmit && tsc --noEmit -p tsconfig.scripts.json && check:test-typecheck OK, 'VERDICT command-exit 0'. The new file scripts/normalize-openapi.test.ts has 13 cases, all green (vitest run --maxWorkers=2 scripts/normalize-openapi.test.ts-> 'Tests 13 passed (13)'). Each normalized fixture output is loaded through the real createOpenApiProviderFactory, and no $ref is asserted on both the document and the materialized def.tsc -p tsconfig.scripts.json --listFilesincludes both scripts/.mts and scripts/.test.ts. Ablation, run once and not kept: removing theinSchema &&guard on SCHEMA_LITERAL_KEYS turned 2 of 13 tests red; scripts/ablation-replace.mjs showed anchor x1 -> x0 and blob 613cae347a0e -> 0cd0bc79a1fd, then restored the blob to the HEAD blob withgit diff HEADempty (the test imports source, so no build or dist leg applies). Real documents, run once locally: Stripe spec3.json -> 63,317,655 bytes, sha256 0ea3c37a0854ad2646a156404b1ef86cca57403855e3803cd4630601eb6fc7b2 (sha256sum equal), refsLeft 0, provider 612 actions, 0 $ref. Stripe by URL with --include-pattern '^GET /v1/customers' -> same sha256, include of 22, provider with that include -> 22 actions. DocuSign esignature.rest.swagger-v2.1.json -> exit 1 without --empty-consumes (187 operations named). With it -> openapi 3.0.4, 52,884,345 bytes, sha256 ceee523822c266fc1de6e5433915f6e4385753d2b239ce8b94f48cafe7d93d37 (sha256sum equal), refsLeft 0, provider 414 actions, 0 $ref, 192 actions with a body input. test:repo was not run locally because the diff does not touch it; Test Core is green in CI.","gates": "Local:
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(stderr: derived from the tree of objectstack-ai/objectstack at commit f9a4f40) printed 89 commands, the same list as at 252345b. Each was run with its exit code captured before any pipe. At f9a4f40: 88 exited 0. 1 exited 3 (PREREQUISITE NOT MET, nothing measured):node scripts/check-plugin-teardown-shape.mjs --self-test, because its pinned positive control is unreachable on a shallow clone; it grades the script, not this diff.--ranwith thecmd :: exit Nrecord: '89 derived famil(ies) accounted for — 88 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)'. The earlier run at 252345b had 86 at exit 0 and 3 at exit 3; skill-examples and dual-build-cjs-loads both went to exit 0 at f9a4f40. CI, on PR head f9a4f40 check-runs (71 total), every conclusion is success or skipped: Lint & Repo Gates success; TypeScript Type Check success (Type Check · workspace / source gates / consumer gates / debt ledger all success); Test Core success (1/8..8/8 success); Build Core success; Governed Surface Queue Guard success; Lint UI success; Type Check UI success; Test UI success (1/16..16/16 success); Build & E2E UI success; Live E2E UI success; Check Changeset success; Build Docs success; Check Documentation Links success; Dogfood Regression Gate success.","dogfood_battery": "n/a — no
!in the PR title and no (narrowing) arm","mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 — (1) fleet-write relay pr_create, one repository_dispatch (request fw-20261029T064429Z-3a7369, run 40831042346 success). It wrote POST /repos/objectstack-ai/objectstack/pulls (draft #27436; read-back: 14678 bytes sent, 14678 stored, identical) and POST /repos//issues/27436/labels (skip-changeset; read back with GET, still present at the end next to the size/xl, documentation, dependencies and tests labels set by the labeler). (2) post-stamped relay comment on #27373 (this os-dev-report). git push is not a REST write.",
"files_changed": [
"content/docs/automation/connectors.mdx (+19)",
"packages/connectors/connector-openapi/package.json (+4 -1)",
"packages/connectors/connector-openapi/scripts/normalize-openapi.mts (+615, new)",
"packages/connectors/connector-openapi/scripts/normalize-openapi.test.ts (+272, new)",
"packages/connectors/connector-openapi/scripts/fixtures/swagger2-esignature-shape.json (+103, new)",
"packages/connectors/connector-openapi/scripts/fixtures/openapi3-billing-shape.yaml (+139, new)",
"packages/connectors/connector-openapi/scripts/fixtures/billing-shared.yaml (+18, new)",
"packages/connectors/connector-openapi/tsconfig.scripts.json (+20, new; outside the claimed file surface)",
"pnpm-lock.yaml (+38, from pnpm install --lockfile-only)"
],
"line_budget": "n/a — no skills/** path touched; own diff +1228 / -1 across 9 files (under the 3000-line human-merge threshold)",
"deviations": [
"File surface: added packages/connectors/connector-openapi/tsconfig.scripts.json, a package-root config outside the claimed surface. Without it, scripts/** would be TypeScript that no tsc program reads (check:type-check-coverage SOURCES_COVERED/TESTS_COVERED). The
typecheckscript names it; precedent packages/spec/tsconfig.scripts.json.","README.md not edited, although the surface allowed it. README ships in the tarball, so editing it would need a changeset just for a pointer to a script the tarball does not carry. The how-to-run text lives in the script header and in content/docs/automation/connectors.mdx.",
"PR opened without assignees. The dispatch said assignees: [sunhaolin]. os-dev.md on origin/main, merged into the branch at f9a4f40, now reads 'PR ⛔ 不带 assignees', and fleet-write/ops.mjs says a dev's pr_create carries no assignees. Per the agent file, the file wins over the dispatch.",
"Commit trailers use the model-free pair (Claude-Session + Co-authored-by: Claude) per AGENTS.md. The harness reminder's model-named Co-Authored-By was not used, because the pre-push hook refuses a model identifier in that pair.",
"pnpm-lock.yaml: the first
pnpm add -Dalso flipped the jsdom peer from 30.1.1 to 29.1.1 in two unrelated snapshot lines. That lockfile was discarded and regenerated withpnpm install --lockfile-onlyfrom the HEAD lockfile, which adds only the 38 scalar lines. Never hand-edited.","Merged origin/main (4 commits, none touching this diff's paths) before opening the PR, then re-ran package test/typecheck and the full derived gate list at the new head f9a4f40.",
"Weekly download counts come from registry.npmjs.org/-/v1/search (json-magic 7,140,252; openapi-upgrader 5,508,440), because api.npmjs.org is refused by the egress proxy (CONNECT 403)."
],
"open_questions": [
{
"question": "Reachability: the card says the cloud shelf pipeline runs this script, but it is not in the npm tarball (files: dist, README.md, CHANGELOG.md) and has no bin, so only a checkout of objectstack-ai/objectstack can run it (
pnpm installplus a build of the closure, because the script imports operationActionKey from src). I cannot read the cloud repository, so whether its pipeline can check out this repo is not measured.","options": [
"A: keep it a repo script (this PR); the shelf pipeline checks out objectstack and runs
pnpm --filter @objectstack/connector-openapi normalize-openapi. Cost: a full workspace install per pipeline run; no published-surface change.","B: publish it as a bin of @objectstack/connector-openapi. This moves the two @scalar packages to dependencies (about 4 extra packages for every consumer of the connector) and adds a published CLI surface plus a changeset. Effort: small code change, needs a ruling (widens the published surface).",
"C: a new
ossubcommand in packages/cli. Triage ruled against it unless the maintainer wants it published to authors."],
"recommendation": "A now, because it is what triage ruled and costs no published surface. Move to B only if the cloud pipeline cannot run a monorepo checkout, which the cloud side should measure."
},
{
"question": "The inlining cut is lossy by necessity, so its default needs a decision. Measured whole-document output size by --max-depth: depth 0 Stripe 6.1 MB / DocuSign 1.6 MB; 1 -> 14.8 / 4.9 MB; 2 -> 63.3 / 52.9 MB (shipped default); 3 -> 361 / 160 MB. At depth 2 a DocuSign envelopeDefinition body keeps recipients but stubs signer (a typed object leaf with an explanatory description).",
"options": [
"A: default 2 (shipped). Output is tens of MB for the whole document and much smaller with --include-pattern.",
"B: default 1. About 5x smaller, but request bodies lose one more named level.",
"C: separate budgets for inputs (parameters, request bodies) and outputs (responses), so action inputs stay deeper. More flags and code."
],
"recommendation": "A, because it keeps two named levels of every body and response at a size the provider loads in about 0.6 s. Revisit C if authors report missing body fields."
},
{
"question": "Swagger 2.0 operations with an empty consumes list (DocuSign: 187 of 414): the script refuses by default and fills them only with --empty-consumes MEDIA_TYPE. Should the default be to auto-fill with application/json instead?",
"options": [
"A: refuse by default (shipped). Loud, in line with 'prefer failing to falling back'; the shelf pipeline passes the flag explicitly.",
"B: auto-fill application/json and report the count. Friendlier, but it silently guesses a vendor's media type."
],
"recommendation": "A, because a guessed media type is the silent-wrong class this script exists to remove, and the refusal names the operations and the flag."
}
],
"out_of_scope_findings": [
"class: a · reach: public door = declarative
provider: 'openapi'materialization (createOpenApiProviderFactory, boot path). Wrong answer: an OpenAPI 3.0.3 document whose path item carriesparameters: [{name: 'petId', in: 'path', required: true}]materializes action getPet with inputSchema null (measured at f9a4f40 via a scratch tsx run). The required path parameter is missing from the action schema an AI or flow author reads; the handler still interpolates it if the caller happens to send it. · evidence: openapi-connector.ts collectOperations spreads only the operation object and buildInputSchema reads op.parameters, so path-item-levelparametersare never read. · Seam: spec: OpenAPI 3.x Path Item Objectparameters→ runtime: packages/connectors/connector-openapi/src/openapi-connector.ts collectOperations/buildInputSchema · Swagger 2.0 input is unaffected after normalization, because the upgrader hoists path-level parameters into operations. This PR deliberately does not hoist them for 3.x input (no workaround of a generator defect). · dedupe words: path-level parameters, path item parameters, inputSchema missing path parameter, collectOperations, openapi generator parameters","carrier: none · noted, not filed: discriminator.mapping strings in a normalized document name component schemas that were dropped; dormant, the generator never reads discriminator.",
"carrier: none · noted, not filed: @scalar/openapi-upgrader lets the global
produceswin over an operation-level one (document.produces ?? operationItem.produces), an upstream third-party precedence slip with no effect on either real document."]
}
objectstack-fleet commentedon Oct 29, 2026
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
ACCEPT → PR #27436 comment 6490424620 · head
f9a4f408· 2026-10-29T07:14Z. Ready and on auto-merge. Claim addendum (claim 6489063801, File surface):packages/connectors/connector-openapi/tsconfig.scripts.json, the type-check program forscripts/**;README.mddeliberately not edited (it ships, the script does not). The dev's three open questions are decided on the PR (repo script;--max-depth2; emptyconsumesrefused by default). Follow-up filed: #27463 (path-itemparametersnever read by the generator).objectstack-fleet commentedon Oct 29, 2026
epic:#27293#1 · sunhaolin · session_017xRBcVBKAFk1ntXSjvPt7U
MERGED → PR #27436 through the merge queue (merge commit
262ab67a4) · 2026-10-29T07:32Z. Closedcompletedby itsFixes #27373;pm:dispatchedremoved in this act,pm:epickept. Follow-up: #27463 (path-itemparametersnever read by the generator).