Skip to content

feat: source location tracking for all breaking change checks#752

Open
reuvenharrison wants to merge 27 commits intomainfrom
source-location-tracking
Open

feat: source location tracking for all breaking change checks#752
reuvenharrison wants to merge 27 commits intomainfrom
source-location-tracking

Conversation

@reuvenharrison
Copy link
Collaborator

@reuvenharrison reuvenharrison commented Aug 14, 2025

Summary

  • Adds precise source location (file, line, column) to every breaking change reported by oasdiff
  • Each Change now carries BaseSource and RevisionSource with the exact position in the base and revision spec files
  • When origin tracking is enabled via openapi3.IncludeOrigin = true, all 93 checkers report locations
  • --format githubactions uses RevisionSource for inline annotations with file/line/col precision
  • Depends on kin-openapi#1128 for file path propagation
  • Closes Provide source code lines when reporting breaking changes #574

Changes

New types and interfaces

  • checker.Source struct with File, Line, Column fields
  • CommonChange.BaseSource / RevisionSource fields on all change types
  • GetBaseSource() / GetRevisionSource() methods on the Change interface
  • WithSources() builder method on ApiChange, ComponentChange, SecurityChange

Checker updates (93 files)

  • Every NewApiChange(...) call chains .WithSources(baseSource, revisionSource)
  • operationSources() helper computes sources from operation pairs
  • Sources only populated when origin tracking is enabled (backward compatible)

Source precision tiers

Tier 1 — Sub-object Origin:

  • SchemaSources() — source pair from schema Origins
  • ParameterSources() — source pair from parameter Origins
  • ResponseSources() — source pair from response Origins

Tier 2 — Field-level Origin:

  • SchemaFieldSources() — source pair from a specific field within schema Origins (e.g., type: line)
  • ParameterFieldSources() — source pair from a specific field within parameter Origins
  • OperationFieldSources() — source pair from a specific field within operation Origins

Tier 3 — Sequence item Origin:

  • NewSourceFromSequenceItem() — source from a specific item in a sequence field (e.g., a particular enum value)
  • SchemaDeletedItemSources() / SchemaAddedItemSources() — source pair for deleted/added sequence items

Nil convention for missing side

  • Added items: baseSource = nil (didn't exist in base)
  • Deleted items: revisionSource = nil (doesn't exist in revision)
  • "Set" changes (field goes from unset to set): baseSource = nil
  • "Unset" changes (field goes from set to unset): revisionSource = nil
  • NewSourceFromField returns nil when the field doesn't exist in Origin (no fallback to parent)
  • NewSourceFromSequenceItem returns nil when the item isn't found

GitHub Actions formatter migration

  • --format githubactions now uses GetRevisionSource() for file=, line=, col= annotation parameters
  • Annotations with RevisionSource appear inline on PR diffs at the exact line of the change
  • Falls back to operation source file (no line/col) when RevisionSource is nil
  • Removal-type changes (revisionSource = nil) emit ::error title=...::message without file= — they appear in the Actions log but not inline on the diff, since the removed element only exists in the base file which cannot be referenced by GitHub annotations
  • HTTP URLs in source paths are filtered (annotations require local file paths)
  • Dropped endLine/endColumn parameters (never populated)

Other formatter updates

  • JSON and YAML formatters include baseSource/revisionSource in output

Infrastructure

  • diff.go: passes both base and revision operation sources
  • diff/security_schemes.go: added Base/Revision fields for component-level source tracking
  • go.mod: uses oasdiff/kin-openapi fork with origin file tracking
  • yaml3 feat/origin-enhancements: sequence field keys now tracked in origin.Fields (in addition to items in origin.Sequences)

Known limitations

No source locations

  • Global security changes (api-global-security-*) — spec-root-level changes with no operation context; SecurityRequirementsDiff does not store original objects needed for Origins.
  • Operation security changes (api-security-*) — security is a complex array-of-objects not tracked at field level in Origin; pointing to the operation line would be misleading.

No per-item sequence precision

  • x-extensible-enum checkers (check_request_parameter_x_extensible_enum_value_removed.go, check_request_property_x_extensible_enum_value_removed.go) — x-extensible-enum is an extension, not tracked in Origin.Sequences. Uses schema/parameter-level sources.
  • list-of-types checkers (check_request_parameter_list_of_types_changed.go, check_request_property_list_of_types_changed.go, check_response_property_list_of_types_changed.go) — reports aggregated diffs (joined string of all added/deleted types), not per-item. Uses field-level sources.
  • response required property updated (check_response_required_property_updated.go) — iterates property schemas via CheckDeletedPropertiesDiff/CheckAddedPropertiesDiff, not required list items. Already uses propertySource which points to the property schema itself.

Removal-type changes and GitHub annotations

  • GitHub Actions ::error file=,line= annotations can only reference lines in the head (revision) commit. There is no mechanism to annotate a line in the base file before a change.
  • Removal-type breaking changes (endpoint removed, property removed, etc.) only have a BaseSource — the removed element doesn't exist in the revision file.
  • These changes emit ::error without file= so they appear in the Actions log and checks summary, but not inline on the PR diff.
  • For inline annotation of removals, the PR Review API with side: LEFT can comment on deleted lines in the diff — this is a separate, richer mechanism suitable for a paid tier.

Other

  • Origin key matching is case-sensitive. Origin tracks YAML keys as literally written. If a YAML file uses readonly instead of the canonical readOnly, the source lookup won't match. This matches the OpenAPI specification which requires camelCase field names.

Test plan

  • 4 new source tracking tests covering all checker patterns
  • 14 existing source tracking tests for add/remove checkers
  • 6 tests for sub-object and field-level source helpers
  • Formatter tests updated to use RevisionSource instead of deprecated fields
  • Full test suite passes
  • go vet clean
  • Backward compatible - no sources populated when IncludeOrigin is false

🤖 Generated with Claude Code

@codecov-commenter
Copy link

codecov-commenter commented Aug 14, 2025

Codecov Report

❌ Patch coverage is 84.72776% with 115 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.25%. Comparing base (9ee0f2b) to head (0327c53).

Files with missing lines Patch % Lines
checker/source.go 78.32% 31 Missing and 13 partials ⚠️
checker/check_request_property_min_set.go 14.28% 4 Missing and 2 partials ⚠️
checker/check_request_property_min_updated.go 44.44% 3 Missing and 2 partials ⚠️
checker/check_response_status_updated.go 68.75% 3 Missing and 2 partials ⚠️
...cker/check_request_property_min_items_increased.go 42.85% 3 Missing and 1 partial ⚠️
checker/check_request_property_min_items_set.go 0.00% 4 Missing ⚠️
checker/check_response_property_max_increased.go 0.00% 4 Missing ⚠️
checker/check_response_property_min_decreased.go 0.00% 4 Missing ⚠️
checker/check_response_property_min_items_unset.go 0.00% 4 Missing ⚠️
checker/security_change.go 0.00% 4 Missing ⚠️
... and 17 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #752      +/-   ##
==========================================
- Coverage   89.55%   89.25%   -0.30%     
==========================================
  Files         239      240       +1     
  Lines       12154    12627     +473     
==========================================
+ Hits        10884    11270     +386     
- Misses        840      903      +63     
- Partials      430      454      +24     
Flag Coverage Δ
unittests 89.25% <84.72%> (-0.30%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@oasdiff oasdiff deleted a comment from claude bot Feb 17, 2026
@oasdiff oasdiff deleted a comment from claude bot Feb 17, 2026
@oasdiff oasdiff deleted a comment from claude bot Feb 17, 2026
@oasdiff oasdiff deleted a comment from claude bot Feb 17, 2026
@oasdiff oasdiff deleted a comment from claude bot Feb 17, 2026
@oasdiff oasdiff deleted a comment from claude bot Feb 17, 2026
@reuvenharrison reuvenharrison force-pushed the source-location-tracking branch 3 times, most recently from 46ae1e6 to 8410950 Compare February 17, 2026 19:26
Replace NewApiChangeWithSources(args..., base, rev) with
NewApiChange(args...).WithSources(base, rev). This avoids
needing to migrate all ~86 checker functions to a new constructor
and eliminates merge conflicts with the OpenAPI 3.1 branch.

- Add WithSources() method to ApiChange, ComponentChange, SecurityChange
- Add NewSourceFromField() to source.go
- Update callers in check_api_added.go and check_api_removed.go
- Remove NewApiChangeWithSources constructor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@reuvenharrison reuvenharrison force-pushed the source-location-tracking branch from 8410950 to e48ff98 Compare February 17, 2026 19:27
reuvenharrison and others added 4 commits February 18, 2026 08:05
Point to oasdiff/kin-openapi feat/origin-file-tracking branch which
adds file paths to origin location data. Enable IncludeOrigin only
in tests that check source locations instead of globally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NewSourceFromOrigin and NewSourceFromField now use the file path
from the origin Location when available, falling back to
OperationsSourcesMap only when origin doesn't include a file.
This enables precise file tracking for $ref'd schemas from
external files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add .WithSources(baseSource, revisionSource) to every NewApiChange call
across all checker files. When origin tracking is enabled, each breaking
change now carries precise file/line/column for both base and revision
specs.

- Add operationSources() helper to compute base/revision sources
- Only populate sources when origin data is available (backward compatible)
- Update helper functions to return ApiChange for chaining
- Add 4 new tests covering all checker patterns (operation, parameter,
  request property, response property)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@reuvenharrison reuvenharrison changed the title feat: Add source location tracking for breaking changes correlation feat: source location tracking for all breaking change checks Feb 18, 2026
reuvenharrison and others added 7 commits February 18, 2026 13:35
Resolve 53 file conflicts: combine WithSources() (source tracking)
with WithDetails() (media type/deprecation details) from main.
Also add WithSources to new property deprecation checkers from main,
and revert openapi3.Ptr to type-specific helpers for fork compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rebased feat/origin-file-tracking onto post-Ptr master (45db2ad),
so openapi3.Ptr is available. Reverted Uint64Ptr/Float64Ptr
workarounds back to openapi3.Ptr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add NewSourceFromSequenceItem() for looking up individual items in
  scalar sequences (e.g. type: [string, null]) by value via
  Origin.Sequences
- Update kin-openapi dependency to pick up Origin.Sequences and
  Location.Name fields
- Add responseSource() helper for sub-operation-level source locations
  on response status codes
- Enable origin tracking by default in calcDiff

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ches

Updates all dependencies to use the origin tracking feature branches
with sequence origin and Location.Name support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace operation-level operationSources() with finer-grained helpers
(SchemaSources, ParameterSources, ResponseSources) across ~70 checker
files so reported changes point to the specific schema, parameter, or
response rather than the enclosing operation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use SchemaFieldSources() instead of SchemaSources()/ParameterSources()
for ~45 checkers that detect changes to specific scalar fields (type,
format, min, max, pattern, etc.). This points source locations to the
exact YAML field line rather than the parent schema/parameter object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
reuvenharrison and others added 9 commits February 23, 2026 20:15
Adds OperationFieldSources and ParameterFieldSources helpers, and
upgrades remaining checkers that were still at object-level to use
field-level precision:

- API-level: deprecated, operationId, tags, security
- Schema-level: enum, oneOf/anyOf/allOf, discriminator, required, type
- Parameter-level: required
- Fixes NewSourceFromField to fall back to key-level instead of file-only

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Standardize nil for the "missing side" in all add/delete checkers
  (property added → baseSource=nil, property removed → revisionSource=nil)
- Fix set checkers (max_set, min_set, etc.) to nil out baseSource
- Fix unset checkers (max_length_unset, min_items_unset) to nil out revisionSource
- Fix operationId added/removed to nil out the missing side
- Fix pattern added/removed to nil out the missing side
- Add source tracking to component-level checkers (schema removed,
  security component added/removed) with new sourceFromOrigin helper
- Add Base/Revision fields to SecuritySchemesDiff for origin access
- Fix NewSourceFromField: return nil instead of misleading key-level
  fallback when field doesn't exist; also check origin.Sequences for
  sequence-valued fields (required, enum, type array)
- Fix NewSourceFromSequenceItem: return nil instead of file-only fallback
- Simplify SchemaDeletedItemSources/SchemaAddedItemSources to use nil
  for the missing side
- Upgrade request parameter enum value checker to per-item precision
  using SchemaDeletedItemSources/SchemaAddedItemSources
- Add specific item-level sources for response headers, media types,
  parameters, request body, and properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Operation security checkers: remove operation-level sources since
  "security" isn't tracked at field level in Origin. No source is
  better than an imprecise one pointing to the operation line.
- api-operation-id-removed: clear revisionSource in the "changed" case
  (old ID replaced by new) since the change is about what was removed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
yaml3 now includes sequence field keys (required, enum, type array) in
origin.Fields, so NewSourceFromField no longer needs to fall back to
origin.Sequences. Each function has a clear responsibility:
- NewSourceFromField: looks up field key location via origin.Fields
- NewSourceFromSequenceItem: looks up specific item via origin.Sequences

Also updates kin-openapi and yaml3 dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Upgrade 9 checkers to use SchemaDeletedItemSources/SchemaAddedItemSources
for per-item precision instead of SchemaFieldSources field-level precision.

Enum checkers (5 files):
- check_request_property_enum_value_updated.go
- check_response_property_enum_value_removed.go
- check_response_property_enum_value_added.go
- check_request_body_enum_deleted.go
- check_response_mediatype_enum_value_removed.go

Required checkers (4 files):
- check_request_property_required_updated.go
- check_response_property_became_required.go
- check_response_property_became_optional.go
- check_request_header_property_became_required.go

Each now points to the specific item in the sequence (e.g., the exact
enum value or required property name) rather than the field key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use GetRevisionSource() for precise file/line/col annotations instead
of deprecated GetSourceFile()/GetSourceLine()/GetSourceColumn() methods.
Falls back to operation source file when RevisionSource is nil (removal
changes appear in Actions log without inline annotation).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use requestBodyFieldSources() to point to the `required` field within
the request body origin instead of the operation-level source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

Provide source code lines when reporting breaking changes

2 participants