Skip to content

fix(downgrader): follow path item refs through lowercase additionalOperations keys - #49

Open
dinwwwh wants to merge 4 commits into
mainfrom
claude/clever-ritchie-ohjfrm
Open

dinwwwh wants to merge 4 commits into
mainfrom
claude/clever-ritchie-ohjfrm

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 2, 2026

Copy link
Copy Markdown
Member

Problem

3.2 → 3.1 removes additionalOperations, so a $ref into it has to be inlined. A Path Item $ref is merged only when followsPathItem sees that its pointer lands on a Path Item. That check reads the pointer from the end, and isOperationPointer tried the "fixed method or query" reading first and returned its result without trying the additionalOperations reading.

The official 3.2 schema only forbids uppercase method names as additionalOperations keys, so query or get is a valid key. With such a key, the pointer was read as a fixed method of a Path Item at .../additionalOperations, which is not a Path Item. The $ref was left as written and pointed into the removed additionalOperations in the 3.1 output.

downgradeSpecV32ToV31({
  openapi: '3.2.0',
  paths: {
    '/a': { additionalOperations: { query: { responses, callbacks: { c: { '{$url}': { get: { operationId: 'cg', responses } } } } } } },
    '/b': { $ref: '#/paths/~1a/additionalOperations/query/callbacks/c/%7B$url%7D', summary: 's' },
  },
})
additionalOperations key /b on main /b with this PR
PURGE { summary: 's', get: { operationId: 'cg', … } } unchanged
query or get { $ref: '#/paths/~1a/additionalOperations/query/…', summary: 's' }, dangling { summary: 's', get: { operationId: 'cg', … } }

Fix

isOperationPointer (packages/downgrader/src/shared.ts) now accepts a pointer when either reading lands on a Path Item. It is an OR, not just a reordering: a webhook or callback expression may itself be named additionalOperations, and for #/webhooks/additionalOperations/query/... the fixed-method reading is the right one.

I rejected the broader alternative, followsPathItem = ctx.dangles(ref) && isRecord(ctx.resolve(ref)). It merges targets that are not Path Items (an Operation, a schema, a Responses map, an x- extension) into the Path Item and produces invalid output, which 4 existing tests pin.

Both readings can now run, but the check stays linear in pointer length (at most about 1.5× the old work) and only runs for Path Item $refs.

Tests

tests/v3.2-to-v3.1/spec/removed-parts.test.ts, one it.each over three pointers:

  • #/paths/~1a/additionalOperations/query/callbacks/c/{$url} and #/paths/~1a/additionalOperations/get/callbacks/c/{$url} fail on main.
  • #/webhooks/additionalOperations/query/callbacks/c/{$url} passes on main. It fails if the checks are only reordered with an early return.

Verification

  • pnpm lint, pnpm type:check and pnpm test:coverage (695 tests) are clean on the merge with main.
  • This branch merges cleanly with middleapi/openapi-spec#48, which also builds on isPathItemPointer. The combined suite passes (701 tests).

🤖 Generated with Claude Code

https://claude.ai/code/session_01867kU5KHoqdve4JdNzRsPv


Generated by Claude Code

claude added 4 commits October 2, 2026 09:44
…erations keys

`isOperationPointer` tested for a fixed method or `query` before
`additionalOperations`, so a pointer through a lowercase key such as
`additionalOperations/query` or `additionalOperations/get` (valid in 3.2,
which only forbids uppercase method names there) was not seen as landing
on a Path Item. A path item `$ref` into it was left as written and
dangled once `additionalOperations` was removed.

Check `additionalOperations` first, falling through to the fixed-method
reading for a webhook or callback expression named `additionalOperations`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01867kU5KHoqdve4JdNzRsPv
Revert the lockfile churn a local `pnpm install` added to the previous commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01867kU5KHoqdve4JdNzRsPv
…tests

Join both readings of an operation key with a single `||`, since neither
takes priority, and fold the lowercase-key and webhook cases into one
`it.each` over the pointers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01867kU5KHoqdve4JdNzRsPv
@pullfrog

pullfrog Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

your Pullfrog Router balance is empty, and this repo has no provider key to fall back on, so the agent never ran.

To fix, any one of: add a payment method or top up your Router balance · add a provider API key (GitHub Actions secret or Pullfrog secret) · switch this repo to a free model.

Top up Router → · Model settings → · Setup docs → · Ask in Discord →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | 𝕏

dinwwwh commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

The pullfrog check is red, but this PR didn't cause it. Its comment above says the organization's Pullfrog Router balance is empty, so the agent never ran. The check fails the same way on #48. Code can't fix it: someone has to top up the balance, add a provider key, or switch to a free model in the Pullfrog settings. A re-run would fail the same way until then.

CI on 78279c6 is green: lint_and_typecheck, plus test_matrix on Node 20, 22, 24 and 26.


Generated by Claude Code

@codecov

codecov Bot commented Oct 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants