Skip to content

[pull] main from fern-api:main#762

Merged
pull[bot] merged 30 commits into
code:mainfrom
fern-api:main
Jun 1, 2026
Merged

[pull] main from fern-api:main#762
pull[bot] merged 30 commits into
code:mainfrom
fern-api:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Jun 1, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

fern-support and others added 30 commits June 1, 2026 18:08
Co-authored-by: dsinghvi <10870189+dsinghvi@users.noreply.github.com>
… True (#16155)

* fix(python): derive offset pagination _has_next instead of hardcoding True

The Python SDK generator's offset paginator emitted `_has_next = True`
unconditionally, so `while pager.has_next: pager = pager.next_page()`
loops never terminated. It also ignored the IR's optional
`OffsetPagination.has_next_page` response property — even users who
explicitly declared `has-next-page` got the same hardcoded `True`.

`init_has_next()` now:
- emits `bool(_parsed_response.<path>)` when `has-next-page` is set on
  the pagination config, and
- falls back to `len(_items or []) > 0` otherwise, matching the
  TypeScript generator's `(r?.data ?? []).length > 0` semantics.

* fix(python): harden offset paginator against nested paths and optional responses

Two latent crashes in the offset paginator, surfaced by code review:

1. Nested `has-next-page` paths dereferenced intermediate segments with no
   None-guard. `has-next-page: $response.metadata.hasNext` with a null
   `metadata` raised AttributeError. `get_next_none_safe_condition()` now
   returns the same null-guard expression `CursorPagination` uses, so the
   block runs under `if _parsed_response.metadata is not None:`.

2. `init_custom_vars_pre_next` was a no-op, so endpoints with an optional
   response type left `_has_next`/`_get_next` unbound when the server
   returned a null body — `SyncPager(has_next=_has_next, ...)` then raised
   NameError. Now pre-initializes both to `False`/`None` (matching cursor),
   so the conditional guard always has fallback values.

Neither path is exercised by current seed fixtures (no nested `has-next-page`
in test definitions, and no offset endpoint with `response: optional<T>`),
so snapshots are unchanged.
…15989)

* feat(cli-v2): add `fern api enrich` command for AI-generated examples

Implements the `fern api enrich` command in CLI v2, which merges
x-fern-examples from an overrides file into native OpenAPI example
fields (parameters, request body, response body).

Supports:
- In-place enrichment of OpenAPI specs
- Output to a separate file via -o
- Extracting only enriched examples via --split

Closes FER-8845

* Add AI example flags and split extraction

Make the overrides file optional and add --ai-examples / --disable-ai-examples flags across CLI v1 and v2, with validation errors if AI generation is requested but not available. Add a --split option and support in mergeOpenAPIWithOverrides to export only enriched examples (extractEnrichedExamples implemented). Default output path to in-place when not provided, improve logging, and change applyRequestBodyExample/applyRequestBodyNamedExample to no-op when requestBody is missing instead of creating an empty schema.

* Require --output when --split is used

Add validation in addEnrichCommand to error if the --split flag is true but no --output (-o) path is provided. The CLI now calls cliContext.failAndThrow with a clear message and ConfigError code to guide users to specify an output location for extracted examples.

* fix(cli-v2): close silent-drop, $ref, and case-sensitivity gaps in api enrich

Found via code review of the new `fern api enrich` command. Same fixes are
applied to the legacy CLI's mergeOpenAPIWithOverrides module since the two
modules duplicate the same logic.

- Warn on silent drops: thread a warn callback into the apply helpers so
  request.body/response.body overrides that hit no requestBody, no content,
  or no 2xx/default response are surfaced as `<METHOD> <path>: ...` instead
  of being silently discarded. Same for parameter overrides whose name does
  not match the spec.
- Fix extractEnrichedExamples false positives on $ref parameters: resolve
  $refs on the original side before matching by name/in, so pre-existing
  examples on referenced components are not reported as newly enriched in
  --split output.
- Match header parameter names case-insensitively per RFC 7230.
- Fall back to responses.default in getFirstSuccessResponse when no 2xx
  response is declared.
- Remove the dead --disable-ai-examples flag from both yargs surfaces.

Adds five regression tests covering each fix.

* fix(cli-v2): move biome-ignore inside multi-line applyResponseBodyNamedExample signature

---------

Co-authored-by: Naman Anand <info@buildwithfern.com>
Co-authored-by: patrick thornton <patrickthornton@g.harvard.edu>
feat(ruby-v2): add client-level max_retries constructor parameter
Co-authored-by: iamnamananand996 <31537362+iamnamananand996@users.noreply.github.com>
…pe (#16159)

fix(python-sdk): guard bare unparameterized dict/list/set in construct_type

get_args() returns an empty tuple for bare dict, list, and set types.
The dict branch crashed with ValueError (not enough values to unpack)
and the list/set branches crashed with IndexError (tuple index out of
range). Now each branch checks for empty args and returns the object
unchanged, matching Dict[Any,Any]/List[Any]/Set[Any] semantics.

Resolves FER-10934

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore(cli): serve OpenAPI spec locally in v2 generate ETE test

* chore(cli): pick OpenAPI test-server port dynamically to avoid cross-file collisions
Co-authored-by: patrickthornton <70873350+patrickthornton@users.noreply.github.com>
Co-authored-by: dsinghvi <10870189+dsinghvi@users.noreply.github.com>
…6149)

* chore(cli-generator): add cli-sdk sync pipeline and documentation

- Add generators/cli/scripts/sync-sdk.sh: deterministic projection of
  cli-sdk's workspace Cargo.toml into the vendored single-package
  manifest, rsync of source files under SDK_IGNORE rules, Cargo.lock
  regeneration, and provenance marker (.synced-from).

- Add .github/workflows/sync-cli-sdk.yml: daily cron + workflow_dispatch
  that checks out cli-sdk main HEAD, runs the sync script, opens an
  auto-merging PR if there are changes, and relies on seed tests as the
  trust boundary.

- Update generators/cli/CLAUDE.md with sync documentation covering
  projection rules, manual sync instructions, and the must-rebuild list.

Closes FER-10795

* fix: address Hex Security findings — remove auto-merge/self-approval, pin actions to SHA

- Remove 'Enable auto-merge' and 'Approve PR' steps to require human
  review before externally-sourced code lands in main (high severity)
- Pin peter-evans/create-pull-request to commit SHA instead of mutable
  tag (medium severity)
- Remove unnecessary pull-requests:write permission
- Update CLAUDE.md to reflect human review requirement

* fix: remove dead rsync exclude for src/bin/strip_schema.rs

The exclude pattern used the wrong relative path (rsync paths are
relative to the transfer root, which is $CLI_SDK_DIR/src/). The file
is also required by the projected Cargo.toml [[bin]] entry for
strip-schema, so excluding it would break cargo build.

* fix: remove timestamp from .synced-from to prevent spurious daily PRs

The timestamp changed on every run, causing git diff to always report
changes even when cli-sdk SHA is unchanged. Only the SHA is needed for
provenance — git commit timestamps provide the sync date.

* chore: add cargo build --locked verification step to sync workflow

Validates the projected SDK compiles before opening a PR, so a broken
sync never reaches reviewers.

* fix: use extract_section for workspace version extraction

grep -A2 was fragile — would fail if version wasn't within 2 lines of
the [workspace.package] header. Also removes unused SYNC_DATE variable.

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
fix(cli-generator): define SYNC_DATE in sync-sdk.sh to fix unbound variable error

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…ple validation (#16157)

* fix(cli): preserve variant-inherited keys in discriminated union example validation

* test(cli): cover union/variant schema-disagreement case in extends-overlap fixture
Co-authored-by: dsinghvi <10870189+dsinghvi@users.noreply.github.com>
The ruby-v2 model generator's ObjectGenerator was reading only
`objectDeclaration.properties` and ignoring `extendedProperties`. As a
result, every derived model class — including discriminated union
variants — silently dropped fields contributed by parent types. Flatten
`extendedProperties` into the field list before emit, matching the
existing pattern in WrappedRequestGenerator.
Co-authored-by: dsinghvi <10870189+dsinghvi@users.noreply.github.com>
@pull pull Bot locked and limited conversation to collaborators Jun 1, 2026
@pull pull Bot added the ⤵️ pull label Jun 1, 2026
@pull pull Bot merged commit c9c445f into code:main Jun 1, 2026
10 of 11 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants