Skip to content

sdk-sync: API 2.0.0+ffb34b7 → sdk-java (minor) - #101

Closed
marketdata-sdk-sync[bot] wants to merge 2 commits into
mainfrom
sdk-sync/2.0.0-ffb34b7
Closed

marketdata-sdk-sync[bot] wants to merge 2 commits into
mainfrom
sdk-sync/2.0.0-ffb34b7

Conversation

@marketdata-sdk-sync

@marketdata-sdk-sync marketdata-sdk-sync Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Generated by sdk-auto-sync for API release 2.0.0 (commit ffb34b7).

Changes in the API surface

Snapshots ae2b047c8f6f → c2a31e277345:

~ GET /v1/options/chain/{underlying}/ ?dte: string  (was integer)

What the agent did

tests: Added tests (no production code) for the GET /v1/options/chain dte parameter's type change (int → string with 4 forms). Unit wire tests in OptionsResourceTest cover the 3 new forms — comma-list (ExpirationFilter.dteList), closed range (dteRange), and all 4 comparison operators (dteComparison with GT/GTE/LT/LTE) — plus validation tests for empty/negative/>36500 inputs on these new methods only; the existing single-value dte(int) form and its validation are left untouched as the regression case. Integration tests in OptionsIntegrationTest add one test per form plus boundary-inclusion/exclusion checks, all derived from a live-discovered dte board (no hardcoded dates/values) against AAPL only. Added SdkSyncCompatProbe exercising today's ExpirationFilter.dte(int) and OptionsChainRequest builder usage. The exact wire syntax for the three new forms (dash-joined range, operator-prefixed comparison reusing StrikeFilter.Operator) is my design choice for phase 2 to implement — if the live API's actual encoding disagrees, that's for the implementer to surface.

implement: Implemented the chain endpoint's dte parameter widening (int → string) by adding three additive ExpirationFilter variants — dteList (comma list), dteRange (closed range), dteComparison (reusing StrikeFilter.Operator) — alongside the unchanged dte(int) form, wired into OptionsResource's query-building.

  • Doc of new methods/types with example: docs/options/chain.md updated (signatures + Java example); OptionsAdvancedExample.java demos all three new forms.
  • Doc comment on every new public member: added to dteList/dteRange/dteComparison factories and DteList/DteRange/DteComparison records.
  • One wire test per form/enum value: unit tests cover list, range, and all 4 comparison operators separately (phase 1, unchanged); live-derived boundary tests for each form in OptionsIntegrationTest.
  • Validation holds on every public path: compact constructors on DteList/DteRange/DteComparison enforce non-negative/≤36500/min≤max/non-empty/non-null directly, not only via the static factories; added tests constructing DteList's record directly (empty, null list, null element).
  • No coverage exclusions added; gradle test and gradle build (incl. 100% JaCoCo, Spotless) both green.
  • Examples/docs already showing what changed: docs/options/chain.md and OptionsAdvancedExample.java updated; README.md's chain snippet and OptionsExample.java left unchanged — correct as-is, neither claimed to enumerate every filter form before.
  • Existing parameter keeps accepting everything it did: dte(int) and its validation/error type are untouched.
    Left unedited, not relevant to this delta (no dte-specific content found): docs/client.md, docs/options/quotes.md, docs/options/README.md, docs/options/expirations.md, docs/options/lookup.md, src/test/java/.../JsonResponseParserTest.java, RequestValidationTest.java, docs/adr/ADR-006 and ADR-008 (the sealed-filter convention they establish already covers this extension), docs/.sync-manifest.txt (marked auto-generated, do-not-hand-edit).
    Outside my limits, left as-is: .github/ISSUE_TEMPLATE/bug.yml and other files outside docs/src — not reviewed for dte-specific content per the task's scope.

review fix 1: Fixed the three major findings in OptionsIntegrationTest.java, all about dte comparison/single-value tests that would pass even if the server ignored the filter (front-month default masking an ignored parameter):

  • chainDteSingleValueStillSelectsClosestExpiration: now picks the board's last entry (away from the front) instead of the front-month value, so an ignored dte would return the wrong expiration and fail.
  • chainDteComparisonGteIncludesExactBoundaryDte: bound is now the board's middle entry (above the front), and asserts every returned dte >= bound, not just contains(bound); a front-month-only (ignored-filter) answer now fails the >= check.
  • chainDteComparisonLtExcludesExactBoundaryDte: now asserts every returned dte < bound and that more than one distinct dte comes back, in addition to doesNotContain(max); a front-month-only answer fails the distinct-count check.
    Left unresolved for a person: the GT boundary-semantics question at line 265 (unchanged, as instructed) — it already fails correctly if the filter is ignored, so it was not touched beyond the finding itself being out of scope.
    Did not touch chainDteComparisonLteIncludesExactBoundaryDte, chainDteRangeIncludesInclusiveBoundaries, chainDteRangeExcludesDteBelowRaisedLowerBound, or chainDteListReturnsClosestExpirationPerValue — none were flagged, and each already fails if the filter is ignored (their bound/list values differ from the front-month default).
    gradle test and gradle build (compileIntegrationTestJava included) both pass; no production code, documentation, or other test files changed in this round.

changelog: Added ### Added section to CHANGELOG.md under [Unreleased] listing three new ExpirationFilter static factories: dteList(int...) for comma-separated selection, dteRange(int, int) for closed-range selection, and dteComparison(StrikeFilter.Operator, int) for comparison-operator selection. All reference API 2.0.0, the widening of the dte parameter from integer to string.

Questions for a person

The internal reviewers raised these for a person to answer. They did not block the change, and the coder was not asked to resolve them.

  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 — For a person to settle with the API team: the release text says a comparison operator "(>=30, <45) returns every expiration whose dte falls within the bounds, inclusive", yet chainDteComparisonGtExcludesExactBoardDte (request dte=>N, N an expiration on the live AAPL board) came back without the row at exactly N — i.e. > and < behave strictly, as "the syntax matches strike" implies. Worth confirming which statement the API intends.
  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:341 — For a person, tied to the look-1 question on chainDteComparisonGtExcludesExactBoardDte: the fix added allMatch(d -> d < max) alongside doesNotContain(max), so this test now pins < as strict, which the release text ("within the bounds, inclusive") contradicts even though the live run agrees with it. I asked for that assertion, so it is not a finding against the author; it needs settling with the API team before the docs or tests treat either reading as final.

Files

  • CHANGELOG.md
  • docs/options/chain.md
  • examples/consumer-test/src/main/java/com/marketdata/examples/resources/OptionsAdvancedExample.java
  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java
  • src/main/java/com/marketdata/sdk/OptionsResource.java
  • src/main/java/com/marketdata/sdk/options/ExpirationFilter.java
  • src/test/java/com/marketdata/sdk/OptionsResourceTest.java

Version

Public-surface change computed by the pipeline: minor

  • added: ExpirationFilter.dteList(int...) — New static factory for the comma-separated dte form; no existing call is affected.
  • added: ExpirationFilter.dteRange(int, int) — New static factory for the closed-range dte form; no existing call is affected.
  • added: ExpirationFilter.dteComparison(StrikeFilter.Operator, int) — New static factory for the comparison-operator dte form, reusing StrikeFilter.Operator; no existing call is affected.
  • added: ExpirationFilter.DteList — New permitted subtype of the sealed ExpirationFilter interface; per repo rule this is additive and the sealed parent is not listed as changed, even though an exhaustive switch over ExpirationFilter now needs a new case.
  • added: ExpirationFilter.DteRange — New permitted subtype of the sealed ExpirationFilter interface, additive per repo rule on sealed types.
  • added: ExpirationFilter.DteComparison — New permitted subtype of the sealed ExpirationFilter interface, additive per repo rule on sealed types.

Live-API suite

  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java

Files of this suite changed by this pull request. The pipeline ran the ones the change added or modified against the live API before the pull request, and they passed; this pull request's CI runs the whole suite.

Job log

The full record of this job — every phase, test and build result, review findings and how they were resolved, and each fix round after the PR was opened — is in the job log comment.


4 coder run(s) · 3 review(s) · 111 turns · 14,205,225 tokens · US$ 7.23 · item 5c097984-5f7b-401b-abba-235a3f417767

Agent runs
  • coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 2,355,296 tokens (131,617 in, 2,187,427 cached, 36,252 out) · 26 turns · US$ 1.34 · 6 min 16 s
  • coder · claude-sonnet-5 · 9,034,713 tokens (75,328 in, 8,931,728 cached, 27,657 out) · 52 turns · US$ 2.36 · 7 min 21 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 612,211 tokens (112,442 in, 484,520 cached, 15,249 out) · 9 turns · US$ 1.80 · 3 min 20 s
  • coder · claude-sonnet-5 · 1,814,323 tokens (28,497 in, 1,773,831 cached, 11,995 out) · 9 turns · US$ 0.59 · 2 min 17 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 41,576 tokens (26,924 in, 10,686 cached, 3,966 out) · 2 turns · US$ 0.39 · 55 s
  • security · claude-haiku-4-5-20251001, claude-sonnet-5 · 91,484 tokens (90,625 in, 0 cached, 859 out) · 2 turns · US$ 0.43 · 13 s
  • analyst · claude-haiku-4-5-20251001, claude-sonnet-5 · 93,581 tokens (46,809 in, 44,077 cached, 2,695 out) · 4 turns · US$ 0.24 · 29 s
  • changelog · claude-haiku-4-5-20251001 · 162,041 tokens (29,225 in, 130,091 cached, 2,725 out) · 7 turns · US$ 0.09 · 28 s

tests: Added tests (no production code) for the GET /v1/options/chain dte parameter's type change (int → string with 4 forms). Unit wire tests in OptionsResourceTest cover the 3 new forms — comma-list (ExpirationFilter.dteList), closed range (dteRange), and all 4 comparison operators (dteComparison with GT/GTE/LT/LTE) — plus validation tests for empty/negative/>36500 inputs on these new methods only; the existing single-value dte(int) form and its validation are left untouched as the regression case. Integration tests in OptionsIntegrationTest add one test per form plus boundary-inclusion/exclusion checks, all derived from a live-discovered dte board (no hardcoded dates/values) against AAPL only. Added SdkSyncCompatProbe exercising today's ExpirationFilter.dte(int) and OptionsChainRequest builder usage. The exact wire syntax for the three new forms (dash-joined range, operator-prefixed comparison reusing StrikeFilter.Operator) is my design choice for phase 2 to implement — if the live API's actual encoding disagrees, that's for the implementer to surface.

implement: Implemented the chain endpoint's dte parameter widening (int → string) by adding three additive ExpirationFilter variants — dteList (comma list), dteRange (closed range), dteComparison (reusing StrikeFilter.Operator) — alongside the unchanged dte(int) form, wired into OptionsResource's query-building.
- Doc of new methods/types with example: docs/options/chain.md updated (signatures + Java example); OptionsAdvancedExample.java demos all three new forms.
- Doc comment on every new public member: added to dteList/dteRange/dteComparison factories and DteList/DteRange/DteComparison records.
- One wire test per form/enum value: unit tests cover list, range, and all 4 comparison operators separately (phase 1, unchanged); live-derived boundary tests for each form in OptionsIntegrationTest.
- Validation holds on every public path: compact constructors on DteList/DteRange/DteComparison enforce non-negative/≤36500/min≤max/non-empty/non-null directly, not only via the static factories; added tests constructing DteList's record directly (empty, null list, null element).
- No coverage exclusions added; gradle test and gradle build (incl. 100% JaCoCo, Spotless) both green.
- Examples/docs already showing what changed: docs/options/chain.md and OptionsAdvancedExample.java updated; README.md's chain snippet and OptionsExample.java left unchanged — correct as-is, neither claimed to enumerate every filter form before.
- Existing parameter keeps accepting everything it did: dte(int) and its validation/error type are untouched.
Left unedited, not relevant to this delta (no dte-specific content found): docs/client.md, docs/options/quotes.md, docs/options/README.md, docs/options/expirations.md, docs/options/lookup.md, src/test/java/.../JsonResponseParserTest.java, RequestValidationTest.java, docs/adr/ADR-006 and ADR-008 (the sealed-filter convention they establish already covers this extension), docs/.sync-manifest.txt (marked auto-generated, do-not-hand-edit).
Outside my limits, left as-is: .github/ISSUE_TEMPLATE/bug.yml and other files outside docs/src — not reviewed for dte-specific content per the task's scope.

review fix 1: Fixed the three major findings in OptionsIntegrationTest.java, all about dte comparison/single-value tests that would pass even if the server ignored the filter (front-month default masking an ignored parameter):
- chainDteSingleValueStillSelectsClosestExpiration: now picks the board's last entry (away from the front) instead of the front-month value, so an ignored dte would return the wrong expiration and fail.
- chainDteComparisonGteIncludesExactBoundaryDte: bound is now the board's middle entry (above the front), and asserts every returned dte >= bound, not just contains(bound); a front-month-only (ignored-filter) answer now fails the >= check.
- chainDteComparisonLtExcludesExactBoundaryDte: now asserts every returned dte < bound and that more than one distinct dte comes back, in addition to doesNotContain(max); a front-month-only answer fails the distinct-count check.
Left unresolved for a person: the GT boundary-semantics question at line 265 (unchanged, as instructed) — it already fails correctly if the filter is ignored, so it was not touched beyond the finding itself being out of scope.
Did not touch chainDteComparisonLteIncludesExactBoundaryDte, chainDteRangeIncludesInclusiveBoundaries, chainDteRangeExcludesDteBelowRaisedLowerBound, or chainDteListReturnsClosestExpirationPerValue — none were flagged, and each already fails if the filter is ignored (their bound/list values differ from the front-month default).
gradle test and gradle build (compileIntegrationTestJava included) both pass; no production code, documentation, or other test files changed in this round.

changelog: Added `### Added` section to `CHANGELOG.md` under `[Unreleased]` listing three new `ExpirationFilter` static factories: `dteList(int...)` for comma-separated selection, `dteRange(int, int)` for closed-range selection, and `dteComparison(StrikeFilter.Operator, int)` for comparison-operator selection. All reference API 2.0.0, the widening of the `dte` parameter from integer to string.
@marketdata-sdk-sync marketdata-sdk-sync Bot added minor adds to the SDK's public surface and breaks nothing sdk-sync: in progress sdk-sync is still working on this PR: CI, code review, fixes labels Oct 30, 2026
@marketdata-sdk-sync

marketdata-sdk-sync Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Author

🧾 Job log — sdk-sync

sdk-auto-sync job log

API release 2.0.0 (commit ffb34b7)
SDK sdk-java
Branch sdk-sync/2.0.0-ffb34b7
API surface ae2b047c8f6f → c2a31e277345
Date 2026-10-30 13:10:13Z
Outcome done

Changes in the API surface

~ GET /v1/options/chain/{underlying}/ ?dte: string  (was integer)

1. tests

Coder (26 turns, US$ 1.34, 6 min 16 s):

Ran gradle test --no-daemon --console=plain once: compileTestJava FAILED with 14 "cannot find symbol" errors for ExpirationFilter.dteList/dteRange/dteComparison, as required for phase 1 (red is expected; these methods land in phase 2). No other errors.

Added tests (no production code) for the GET /v1/options/chain dte parameter's type change (int → string with 4 forms). Unit wire tests in OptionsResourceTest cover the 3 new forms — comma-list (ExpirationFilter.dteList), closed range (dteRange), and all 4 comparison operators (dteComparison with GT/GTE/LT/LTE) — plus validation tests for empty/negative/>36500 inputs on these new methods only; the existing single-value dte(int) form and its validation are left untouched as the regression case. Integration tests in OptionsIntegrationTest add one test per form plus boundary-inclusion/exclusion checks, all derived from a live-discovered dte board (no hardcoded dates/values) against AAPL only. Added SdkSyncCompatProbe exercising today's ExpirationFilter.dte(int) and OptionsChainRequest builder usage. The exact wire syntax for the three new forms (dash-joined range, operator-prefixed comparison reusing StrikeFilter.Operator) is my design choice for phase 2 to implement — if the live API's actual encoding disagrees, that's for the implementer to surface.

Gates:

  • compatibility probe on the base, attempt 1: passed (11 s)
  • tests (expected to fail), attempt 1: failed — Execution failed for task ':compileTestJava' (registered by plugin class 'org.gradle.api.plugins.JavaBasePlugin'). (8 s)

2. implement

Coder (52 turns, US$ 2.36, 7 min 21 s):

gradle test --no-daemon --console=plain: BUILD SUCCESSFUL. gradle build --no-daemon --console=plain: BUILD SUCCESSFUL (unit tests, Spotless, 100% JaCoCo line coverage all pass; integrationTest task SKIPPED as it requires live credentials, per repo convention).

Implemented the chain endpoint's dte parameter widening (int → string) by adding three additive ExpirationFilter variants — dteList (comma list), dteRange (closed range), dteComparison (reusing StrikeFilter.Operator) — alongside the unchanged dte(int) form, wired into OptionsResource's query-building.

  • Doc of new methods/types with example: docs/options/chain.md updated (signatures + Java example); OptionsAdvancedExample.java demos all three new forms.
  • Doc comment on every new public member: added to dteList/dteRange/dteComparison factories and DteList/DteRange/DteComparison records.
  • One wire test per form/enum value: unit tests cover list, range, and all 4 comparison operators separately (phase 1, unchanged); live-derived boundary tests for each form in OptionsIntegrationTest.
  • Validation holds on every public path: compact constructors on DteList/DteRange/DteComparison enforce non-negative/≤36500/min≤max/non-empty/non-null directly, not only via the static factories; added tests constructing DteList's record directly (empty, null list, null element).
  • No coverage exclusions added; gradle test and gradle build (incl. 100% JaCoCo, Spotless) both green.
  • Examples/docs already showing what changed: docs/options/chain.md and OptionsAdvancedExample.java updated; README.md's chain snippet and OptionsExample.java left unchanged — correct as-is, neither claimed to enumerate every filter form before.
  • Existing parameter keeps accepting everything it did: dte(int) and its validation/error type are untouched.
    Left unedited, not relevant to this delta (no dte-specific content found): docs/client.md, docs/options/quotes.md, docs/options/README.md, docs/options/expirations.md, docs/options/lookup.md, src/test/java/.../JsonResponseParserTest.java, RequestValidationTest.java, docs/adr/ADR-006 and ADR-008 (the sealed-filter convention they establish already covers this extension), docs/.sync-manifest.txt (marked auto-generated, do-not-hand-edit).
    Outside my limits, left as-is: .github/ISSUE_TEMPLATE/bug.yml and other files outside docs/src — not reviewed for dte-specific content per the task's scope.

Gates:

  • tests, attempt 1: passed (6 s)
  • build, attempt 1: passed (7 s)
  • compatibility probe, attempt 1: passed (8 s)
  • live API suite, attempt 1: passed (32 s) — gradle integrationTest --rerun --no-daemon --console=plain --tests com.marketdata.sdk.OptionsIntegrationTest

3. review 1

Reviewer (9 turns, US$ 1.80, 3 min 20 s): 3 blocking finding(s)

The change adds three permitted subtypes (DteList, DteRange, DteComparison) plus factories to the sealed ExpirationFilter, renders them on the wire, and covers every form and all four operators with wire tests, record-constructor validation tests (empty, null, null element, 0..36500 bounds) and docs/example updates; the public surface stays additive and dte(int) keeps its old error behaviour. Three of the new live tests, however, assert something the unfiltered chain response already satisfies, so they cannot fail if the server ignores dte. One documentation point (the word "inclusive" applied to the <45 comparison form) needs a person to settle with the API team.

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:144 — chainDteSingleValueStillSelectsClosestExpiration picks discoverDteBoard().get(0) — the nearest expiration — and asserts the returned row has that dte, which is what the chain returns with no expiration filter at all (docs/options/chain.md: "if omitted, the API narrows to the front month"). The test therefore passes unchanged if the server ignores dte. Pick a value away from the front of the board (e.g. the middle or last entry) or compare against the same call made without the filter in the test.
  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:245 — chainDteComparisonGteIncludesExactBoundaryDte uses the board minimum as the bound and only asserts contains(n); the default, unfiltered front-month response carries exactly that dte, so the assertion holds even if >=n was ignored. Use a bound above the front of the board and assert every returned dte is >= the bound (plus contains(bound)), so a front-month-only answer fails.
  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:329 — chainDteComparisonLtExcludesExactBoundaryDte asserts only doesNotContain(max) for <max; a response that ignored dte (front-month only, dte = board minimum) also satisfies that. Add an assertion only an honoured filter satisfies — e.g. every returned dte < max and the result spans more than one distinct dte, or it contains the next-highest board value.
  • [minor · question for a person] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 — For a person to settle with the API team: the release text says a comparison operator "(>=30, <45) returns every expiration whose dte falls within the bounds, inclusive", yet chainDteComparisonGtExcludesExactBoardDte (request dte=>N, N an expiration on the live AAPL board) came back without the row at exactly N — i.e. > and < behave strictly, as "the syntax matches strike" implies. Worth confirming which statement the API intends.

4. review fix 1

Findings sent back to the coder:

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:144 — chainDteSingleValueStillSelectsClosestExpiration picks discoverDteBoard().get(0) — the nearest expiration — and asserts the returned row has that dte, which is what the chain returns with no expiration filter at all (docs/options/chain.md: "if omitted, the API narrows to the front month"). The test therefore passes unchanged if the server ignores dte. Pick a value away from the front of the board (e.g. the middle or last entry) or compare against the same call made without the filter in the test.
  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:245 — chainDteComparisonGteIncludesExactBoundaryDte uses the board minimum as the bound and only asserts contains(n); the default, unfiltered front-month response carries exactly that dte, so the assertion holds even if >=n was ignored. Use a bound above the front of the board and assert every returned dte is >= the bound (plus contains(bound)), so a front-month-only answer fails.
  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:329 — chainDteComparisonLtExcludesExactBoundaryDte asserts only doesNotContain(max) for <max; a response that ignored dte (front-month only, dte = board minimum) also satisfies that. Add an assertion only an honoured filter satisfies — e.g. every returned dte < max and the result spans more than one distinct dte, or it contains the next-highest board value.

Questions for a person, shown to the coder and not for it to resolve:

  • [minor · question for a person] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 — For a person to settle with the API team: the release text says a comparison operator "(>=30, <45) returns every expiration whose dte falls within the bounds, inclusive", yet chainDteComparisonGtExcludesExactBoardDte (request dte=>N, N an expiration on the live AAPL board) came back without the row at exactly N — i.e. > and < behave strictly, as "the syntax matches strike" implies. Worth confirming which statement the API intends.

Coder (9 turns, US$ 0.59, 2 min 17 s):

gradle test --no-daemon --console=plain: BUILD SUCCESSFUL. gradle build --no-daemon --console=plain: BUILD SUCCESSFUL (compileIntegrationTestJava compiles the revised assertions; integrationTest task SKIPPED, same as before, since it requires live credentials).

Fixed the three major findings in OptionsIntegrationTest.java, all about dte comparison/single-value tests that would pass even if the server ignored the filter (front-month default masking an ignored parameter):

  • chainDteSingleValueStillSelectsClosestExpiration: now picks the board's last entry (away from the front) instead of the front-month value, so an ignored dte would return the wrong expiration and fail.
  • chainDteComparisonGteIncludesExactBoundaryDte: bound is now the board's middle entry (above the front), and asserts every returned dte >= bound, not just contains(bound); a front-month-only (ignored-filter) answer now fails the >= check.
  • chainDteComparisonLtExcludesExactBoundaryDte: now asserts every returned dte < bound and that more than one distinct dte comes back, in addition to doesNotContain(max); a front-month-only answer fails the distinct-count check.
    Left unresolved for a person: the GT boundary-semantics question at line 265 (unchanged, as instructed) — it already fails correctly if the filter is ignored, so it was not touched beyond the finding itself being out of scope.
    Did not touch chainDteComparisonLteIncludesExactBoundaryDte, chainDteRangeIncludesInclusiveBoundaries, chainDteRangeExcludesDteBelowRaisedLowerBound, or chainDteListReturnsClosestExpirationPerValue — none were flagged, and each already fails if the filter is ignored (their bound/list values differ from the front-month default).
    gradle test and gradle build (compileIntegrationTestJava included) both pass; no production code, documentation, or other test files changed in this round.

Gates:

  • tests, attempt 1: passed (7 s)
  • build, attempt 1: passed (7 s)
  • compatibility probe, attempt 1: passed (7 s)
  • live API suite, attempt 1: passed (39 s) — gradle integrationTest --rerun --no-daemon --console=plain --tests com.marketdata.sdk.OptionsIntegrationTest

5. review 2

Reviewer (2 turns, US$ 0.39, 55 s): approved

All three blocking findings from look 1 are fixed as asked: chainDteSingleValueStillSelectsClosestExpiration now uses the last board entry, chainDteComparisonGteIncludesExactBoundaryDte uses a bound above the front month with allMatch(d -> d >= bound), and chainDteComparisonLtExcludesExactBoundaryDte adds allMatch(d -> d < max) and a distinct-count > 1 assertion — each would now fail if the server ignored dte. The new size preconditions (2 and 3 distinct expirations) are safe for AAPL and fail loudly rather than vacuously, and nothing else in the file was weakened. One point remains for a person: the </> strictness the tests assert is the disagreement already raised in look 1.

  • [minor · question for a person] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:341 — For a person, tied to the look-1 question on chainDteComparisonGtExcludesExactBoardDte: the fix added allMatch(d -> d < max) alongside doesNotContain(max), so this test now pins < as strict, which the release text ("within the bounds, inclusive") contradicts even though the live run agrees with it. I asked for that assertion, so it is not a finding against the author; it needs settling with the API team before the docs or tests treat either reading as final.

6. security

Security (2 turns, US$ 0.43, 13 s): approved

This change adds new dte filter forms (list, range, comparison) to ExpirationFilter and wires them into the chain request's query string via integer-typed, range-validated (0-36500) values and a closed enum operator — no user-controlled strings reach the URL unescaped, and the existing query-encoding layer (untouched by this diff) continues to percent-encode reserved characters. No credentials, logging, dependency, or build/CI changes are present in the diff. I found no security issue.

  • no findings

7. assess

Analyst (4 turns, US$ 0.24, 29 s) — public surface:

The change only adds to ExpirationFilter: three new static factories (dteList, dteRange, dteComparison) and three new sealed-interface permitted subtypes (DteList, DteRange, DteComparison) carrying the API's three new dte wire forms. The existing dte(int) factory, the Dte record, and every other public type and method (OptionsChainRequest, OptionsResource.chain/chainAsync, etc.) are untouched; the only non-public change is a new private branch in OptionsResource.applyExpirationFilter, which is not part of the public surface. The compatibility probe's calls (ExpirationFilter.dte(int), OptionsChainRequest.builder/build/symbol/expirationFilter) are unaffected.

  • added: ExpirationFilter.dteList(int...) — New static factory for the comma-separated dte form; no existing call is affected.
  • added: ExpirationFilter.dteRange(int, int) — New static factory for the closed-range dte form; no existing call is affected.
  • added: ExpirationFilter.dteComparison(StrikeFilter.Operator, int) — New static factory for the comparison-operator dte form, reusing StrikeFilter.Operator; no existing call is affected.
  • added: ExpirationFilter.DteList — New permitted subtype of the sealed ExpirationFilter interface; per repo rule this is additive and the sealed parent is not listed as changed, even though an exhaustive switch over ExpirationFilter now needs a new case.
  • added: ExpirationFilter.DteRange — New permitted subtype of the sealed ExpirationFilter interface, additive per repo rule on sealed types.
  • added: ExpirationFilter.DteComparison — New permitted subtype of the sealed ExpirationFilter interface, additive per repo rule on sealed types.

Computed by the pipeline: minor; changelog entry written

8. changelog

Coder (7 turns, US$ 0.09, 28 s):

green — entry added to CHANGELOG.md in Keep a Changelog format, matching house style.

Added ### Added section to CHANGELOG.md under [Unreleased] listing three new ExpirationFilter static factories: dteList(int...) for comma-separated selection, dteRange(int, int) for closed-range selection, and dteComparison(StrikeFilter.Operator, int) for comparison-operator selection. All reference API 2.0.0, the widening of the dte parameter from integer to string.

Gates:

  • build, attempt 1: passed (7 s)

Questions for a person

The internal reviewers raised these for a person to answer. They did not block the change, and the coder was not asked to resolve them.

  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 — For a person to settle with the API team: the release text says a comparison operator "(>=30, <45) returns every expiration whose dte falls within the bounds, inclusive", yet chainDteComparisonGtExcludesExactBoardDte (request dte=>N, N an expiration on the live AAPL board) came back without the row at exactly N — i.e. > and < behave strictly, as "the syntax matches strike" implies. Worth confirming which statement the API intends.
  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:341 — For a person, tied to the look-1 question on chainDteComparisonGtExcludesExactBoardDte: the fix added allMatch(d -> d < max) alongside doesNotContain(max), so this test now pins < as strict, which the release text ("within the bounds, inclusive") contradicts even though the live run agrees with it. I asked for that assertion, so it is not a finding against the author; it needs settling with the API team before the docs or tests treat either reading as final.

Totals

  • Bump: minor
  • Coder runs: 4
  • Review runs: 3
  • Gates run: 11 (1 failed)
  • Files changed by the agent: 7
  • Turns: 111
  • Tokens: 14,205,225
  • Estimated cost: US$ 7.23
  • Time: 23 min 54 s (agents 21 min 19 s, gates 2 min 19 s)

Agent runs

  • coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 2,355,296 tokens (131,617 in, 2,187,427 cached, 36,252 out) · 26 turns · US$ 1.34 · 6 min 16 s
  • coder · claude-sonnet-5 · 9,034,713 tokens (75,328 in, 8,931,728 cached, 27,657 out) · 52 turns · US$ 2.36 · 7 min 21 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 612,211 tokens (112,442 in, 484,520 cached, 15,249 out) · 9 turns · US$ 1.80 · 3 min 20 s
  • coder · claude-sonnet-5 · 1,814,323 tokens (28,497 in, 1,773,831 cached, 11,995 out) · 9 turns · US$ 0.59 · 2 min 17 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 41,576 tokens (26,924 in, 10,686 cached, 3,966 out) · 2 turns · US$ 0.39 · 55 s
  • security · claude-haiku-4-5-20251001, claude-sonnet-5 · 91,484 tokens (90,625 in, 0 cached, 859 out) · 2 turns · US$ 0.43 · 13 s
  • analyst · claude-haiku-4-5-20251001, claude-sonnet-5 · 93,581 tokens (46,809 in, 44,077 cached, 2,695 out) · 4 turns · US$ 0.24 · 29 s
  • changelog · claude-haiku-4-5-20251001 · 162,041 tokens (29,225 in, 130,091 cached, 2,725 out) · 7 turns · US$ 0.09 · 28 s

Code review fix 1 — 2026-10-30 13:34:18Z

Outcome: done

Time: 4 min 38 s (agents 3 min 41 s, gates 57 s)

1. code review fix 1

The code review asked for changes:

  • [Blocking] Comparison boundaries remain contradictory (src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:228-266)
    The tests quote comparison bounds as inclusive but assert that GT and LT exclude an exact boundary, and the PR explicitly leaves that contract for the API team to settle. The passing live run establishes current server behavior but does not resolve the authoritative contract. Confirm the semantics, then either retain strict assertions and correct the canonical wording or update the SDK tests and documentation for inclusive behavior.
    Evidence: The diff says both // "a comparison operator (>=30, <45) returns every expiration whose dte falls within the bounds, inclusive." and // > is strict: it must drop the row whose dte exactly equals the bound, unlike >=.
    Public-surface comparison:

Coder (25 turns, US$ 0.56, 2 min 20 s):

gradle test --no-daemon --console=plain: BUILD SUCCESSFUL. gradle build --no-daemon --console=plain: BUILD SUCCESSFUL (unit tests, Spotless, JaCoCo all green; integrationTest SKIPPED per repo convention, requires live credentials).

Fixed the one blocking finding (contradictory boundary wording in OptionsIntegrationTest.java): the comments on chainDteComparisonGteIncludesExactBoundaryDte, chainDteComparisonGtExcludesExactBoundaryDte, and chainDteComparisonLtExcludesExactBoundaryDte quoted the release text's "...falls within the bounds, inclusive" next to assertions that >/< are strict, which read as contradictory. Checked StrikeFilter.java's own javadoc ("Match strikes satisfying operator price") and ExpirationFilter.java's existing dteComparison javadoc (which already says only "the syntax matches strike", never claims inclusive) — the strict >/< behavior the tests assert is what "the syntax matches strike" implies, and production code/docs never asserted the "inclusive" reading for comparisons in the first place (only dteRange is documented as inclusive, correctly). This is the second case in the brief: the release text is ambiguous/self-contradictory, and the real API does what the "matches strike" statement implies, so I reworded the three test comments to state the strict/inclusive split in those terms instead of quoting the inclusive phrase unqualified; the assertions themselves (already fixed in review round 1) were not weakened. The strictness-vs-release-text question itself is unresolved and still needs a person to confirm with the API team — this matches the two "question for a person" items already on record in .sdk-sync/job-log.md (lines tied to chainDteComparisonGtExcludesExactBoardDte), which this fix does not and cannot settle. No production code, CHANGELOG, or other docs changed — no behavior or public-surface change resulted from this fix.

Gates:

  • tests, attempt 1: passed (6 s)
  • build, attempt 1: passed (7 s)
  • compatibility probe, attempt 1: passed (8 s)
  • live API suite, attempt 1: passed (36 s) — gradle integrationTest --rerun --no-daemon --console=plain --tests com.marketdata.sdk.OptionsIntegrationTest

2. review 1

Reviewer (8 turns, US$ 0.61, 1 min 21 s): approved

The fix removes the contradiction the reviewer flagged by replacing the "inclusive" quotation in the three comparison tests with the API's "the syntax matches strike", which this repository already documents via StrikeFilter.Operator (GT(">"), LT("<")) — the permitted route when the API's blanket word does not fit the form it is applied to. No assertion was weakened, the strict tests still fail if the server ignored dte, and no SDK documentation (ExpirationFilter javadoc, docs/options/chain.md) claims inclusivity for the comparison form, so nothing is left stale. Behaviour and public surface are unchanged, so no CHANGELOG update is required.

  • [minor · question for a person] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:276 — For a person, not the author: the API's 2.0.0 text applies the word "inclusive" to >=30, <45 as a group, while the live run shows > and < are strict (which is what "the syntax matches strike" implies). Worth having the API team correct the upstream wording so the next SDK does not re-litigate it.

3. security

The pipeline:

  • the security reviewer did not run: the fix only touched tests and documentation

Questions for a person

The internal reviewers raised these for a person to answer. They did not block the change, and the coder was not asked to resolve them.

  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:276 — For a person, not the author: the API's 2.0.0 text applies the word "inclusive" to >=30, <45 as a group, while the live run shows > and < are strict (which is what "the syntax matches strike" implies). Worth having the API team correct the upstream wording so the next SDK does not re-litigate it.

@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.29%. Comparing base (ff83a90) to head (aea4001).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #101      +/-   ##
============================================
+ Coverage     98.27%   98.29%   +0.02%     
- Complexity     1043     1054      +11     
============================================
  Files           123      123              
  Lines          2606     2645      +39     
  Branches        301      309       +8     
============================================
+ Hits           2561     2600      +39     
  Partials         45       45              
Files with missing lines Coverage Δ
.../main/java/com/marketdata/sdk/OptionsResource.java 98.57% <100.00%> (+0.04%) ⬆️
...a/com/marketdata/sdk/options/ExpirationFilter.java 98.11% <100.00%> (+3.11%) ⬆️

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 47b35ea...aea4001. Read the comment docs.

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

@marketdata-code-review

marketdata-code-review Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

⛔ Blocked — Code review

The patch adds list, range, and comparison forms for the options-chain DTE filter while preserving the existing single-value form. Version impact: minor; all six public symbols are additive. The implementation codifies strict comparison semantics even though the API release language remains contradictory.

Rating patch 3/6 (incomplete) · proof 6/6 (comprehensive) · overall 3/6

Decision needed

What are the intended boundary semantics for DTE comparison operators?

  • Treat > and < as strict and >= and <= as inclusive.
  • Treat every comparison boundary as inclusive and change the API and SDK behavior accordingly.

Recommended: Use standard operator semantics—strict >/< and inclusive >=/<=—because that matches the live API and the existing strike-filter syntax, then correct the conflicting release language.

Before merge

Findings

Blocking

  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 Resolve DTE comparison boundary semantics (contract, 56e05499)
    The tests and public documentation treat > and < as strict, while the cited API release language says comparison bounds are inclusive. This pins an unsettled API contract into the SDK. Confirm the intended semantics with the API team, then align the canonical documentation and tests.

    src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 — // > is strict, matching StrikeFilter's own ">" semantics

Should fix

  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:146-162 DTE tests race the market clock (tests, 3ca4cfb9)
    Each test discovers live integer DTE values and then makes a separate request that expects those exact values. If US/Eastern midnight occurs between the calls, the server's DTE values change and the suite can fail without a code regression. Make the assertions rollover-safe, for example by retaining and asserting stable expiration dates or using a repository clock helper.

    src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:146 — List<Integer> board = discoverDteBoard(); followed by a separate live client.options().chain(...) call and exact dte() assertion.

Since last review

  • Resolved: e6a945b9
  • 0 still open, 2 new

Reviewed aea4001 · revision 2 · codex (gpt-5.6-sol) · claude unavailable · rules: default, sdk

code review fix 1: Fixed the one blocking finding (contradictory boundary wording in `OptionsIntegrationTest.java`): the comments on `chainDteComparisonGteIncludesExactBoundaryDte`, `chainDteComparisonGtExcludesExactBoundaryDte`, and `chainDteComparisonLtExcludesExactBoundaryDte` quoted the release text's "...falls within the bounds, inclusive" next to assertions that `>`/`<` are strict, which read as contradictory. Checked `StrikeFilter.java`'s own javadoc ("Match strikes satisfying `operator price`") and `ExpirationFilter.java`'s existing `dteComparison` javadoc (which already says only "the syntax matches strike", never claims inclusive) — the strict `>`/`<` behavior the tests assert is what "the syntax matches strike" implies, and production code/docs never asserted the "inclusive" reading for comparisons in the first place (only `dteRange` is documented as inclusive, correctly). This is the second case in the brief: the release text is ambiguous/self-contradictory, and the real API does what the "matches strike" statement implies, so I reworded the three test comments to state the strict/inclusive split in those terms instead of quoting the inclusive phrase unqualified; the assertions themselves (already fixed in review round 1) were not weakened. The strictness-vs-release-text question itself is unresolved and still needs a person to confirm with the API team — this matches the two "question for a person" items already on record in `.sdk-sync/job-log.md` (lines tied to `chainDteComparisonGtExcludesExactBoardDte`), which this fix does not and cannot settle. No production code, CHANGELOG, or other docs changed — no behavior or public-surface change resulted from this fix.
@marketdata-sdk-sync

Copy link
Copy Markdown
Author

🙋 Needs a person — sdk-sync

Why: git push: error: failed to push some refs to 'https://github.com/MarketDataApp/sdk-java.git'

Pending findings

  • Resolve DTE comparison boundary semantics · Blocking · src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:265 · id 56e05499 · review comment
  • DTE tests race the market clock · Should fix · src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:146-162 · id 3ca4cfb9 · review comment

Decision needed

What are the intended boundary semantics for DTE comparison operators?

  • Treat > and < as strict and >= and <= as inclusive.
  • Treat every comparison boundary as inclusive and change the API and SDK behavior accordingly.

Recommended: Use standard operator semantics—strict >/< and inclusive >=/<=—because that matches the live API and the existing strike-filter syntax, then correct the conflicting release language.

Code-review fix rounds used: 2 of 2 · CI repairs used: 0 of 2

What to do

  1. Fix the pending findings on sdk-sync/2.0.0-ffb34b7, or have a maintainer waive them (@marketdata-code-review waive <id>).
  2. Answer the decision the review raises, in a comment on this PR.
  3. Then mark the PR ready for review and comment @marketdata-code-review re-review: the review bot skips drafts. The pipeline does not touch this PR again.

This pull request is back to draft and labelled sdk-sync: needs human; the release row says PR_NEEDS_HUMAN_REVIEW.

@marketdata-sdk-sync marketdata-sdk-sync Bot added sdk-sync: needs human sdk-sync stopped: a person has to look (see the PR's comments) and removed sdk-sync: in progress sdk-sync is still working on this PR: CI, code review, fixes labels Oct 30, 2026
@MarketDataDev03

Copy link
Copy Markdown
Collaborator

Closing to re-run the SDK sync for API 2.0.0 from the start.

@MarketDataDev03
MarketDataDev03 deleted the sdk-sync/2.0.0-ffb34b7 branch October 30, 2026 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor adds to the SDK's public surface and breaks nothing review: decision needed review: needs changes sdk-sync: needs human sdk-sync stopped: a person has to look (see the PR's comments)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant