Skip to content

fix(oas:sync): resolve path-item $refs so sync stops deleting pages - #47

Draft
cursor[bot] wants to merge 4 commits into
mainfrom
erunion/critical-bug-workflow-3318
Draft

fix(oas:sync): resolve path-item $refs so sync stops deleting pages#47
cursor[bot] wants to merge 4 commits into
mainfrom
erunion/critical-bug-workflow-3318

Conversation

@cursor

@cursor cursor Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor
🚦 Resolves
Data loss: oas:sync / lint --fix deleted valid reference pages when the spec used path-item $refs

🪄 Changes

extractOperations walked the raw OpenAPI document and only counted HTTP methods sitting directly on each path item. A path-level $ref — including the OAS 3.1 components.pathItems form — therefore contributed zero operations.

syncOneOas then treated every existing page for that spec as orphaned and deleted it. Same path for readme lint --fix (via the oas-reference validator).

Concrete trigger: a repo with reference/openapi.json like:

openapi: 3.1.0
paths:
  /pets:
    $ref: '#/components/pathItems/Pets'
components:
  pathItems:
    Pets:
      get:
        operationId: listPets

and a page reference/.../listPets.md with custom body. One readme oas:sync or readme lint --fix permanently deletes that page.

Fix:

  • Resolve internal JSON Pointer $refs (#/components/pathItems/…, #/paths/~1pets, operation-level pointers) before collecting operations.
  • Skip the delete pass when a $ref still cannot be inlined (e.g. a split spec pointing at ./paths/pets.yaml), so "we couldn't see the operation" is not treated as "the operation was removed".

No change to add/skip-on-collision behavior, path sanitization, or webhook handling (still covered by #43).

🧬 QA & Testing

  • Reproduced the deletion against an OAS 3.1 pathItems spec on unmodified main (page gone; changes.deleted = ['Pets/Other/listPets.md']).
  • After the fix, the same spec keeps the custom page, creates the missing sibling operation page, and the oas-reference validator no longer reports Operation not found.
  • Added regression tests in test/oas-sync.test.js (resolution, no-deletion for internal and external $refs) and test/oas-reference.test.js (no false missing-operation).
  • Full suite: 160/160 passing (npm test).
Open in Web View Automation 

extractOperations walked the raw spec and treated a path-level $ref
(OAS 3.1 components.pathItems, or a pointer to another path) as having
zero operations. oas:sync and lint --fix then deleted every matching
reference page.

Resolve internal JSON Pointer $refs before collecting operations, and
skip the delete pass when a $ref still cannot be inlined (e.g. a split
spec that points at ./paths/*.yaml).

Co-authored-by: Jon Ursenbach <erunion@users.noreply.github.com>
@erunion erunion added the bug Something isn't working label Aug 28, 2026 — with Cursor
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/commands/oas-sync.js Replaces raw operation walking with reference-aware extraction, overlays path-item siblings, and disables destructive deletion when operation visibility is incomplete.
test/oas-sync.test.js Adds regression coverage for internal, external, cyclic, inherited-property, sibling, non-object-target, and operation-level reference cases.
test/oas-reference.test.js Verifies that operations supplied through OAS 3.1 path-item references are recognized by cross-file validation.
package.json Adds the oas package used by the new extraction implementation.
package-lock.json Locks the new oas dependency and its transitive dependency graph.

Reviews (4): Last reviewed commit: "refactor(oas:sync): use the oas library ..." | Re-trigger Greptile

Comment thread src/commands/oas-sync.js Outdated
Comment thread src/commands/oas-sync.js Outdated
Comment thread src/commands/oas-sync.js Outdated
- Walk JSON Pointers with Object.hasOwn so #/__proto__ cannot
  escape into Object.prototype and empty the operation set.
- Treat a $ref as unresolved only after following the full chain,
  so a pointer that lands on an external, cyclic, or broken $ref
  still skips the delete pass.
- Overlay OAS 3.1 path-item siblings onto the resolved target
  instead of dropping them (local keys win).

Co-authored-by: Jon Ursenbach <erunion@users.noreply.github.com>
Comment thread src/commands/oas-sync.js Outdated
cursoragent and others added 2 commits September 1, 2026 21:09
A path-item $ref that resolved to an array or scalar used to return
that target and drop sibling operations, while isUnresolvedRef treated
the hop as successful. Sync then deleted the sibling page.

Keep the original object on a non-object target so siblings stay in
the operation map and $ref still trips the delete guard. Also skip
unresolved operation $refs that have no operationId so we do not
invent a synthetic get_pets page.

Co-authored-by: Jon Ursenbach <erunion@users.noreply.github.com>
Replace the hand-rolled JSON Pointer walker and synthetic operationId
helper with `oas`. getPaths() resolves path-item and operation $refs;
Operation supplies getOperationId, getSummary, getDescription, and
getTags.

Keep a thin overlay for OAS 3.1 siblings that sit next to a path-item
$ref (getPaths drops those) and skip the delete pass when a $ref is
still unresolved so we do not wipe pages or invent get_pets stubs.

Co-authored-by: Jon Ursenbach <erunion@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants