Skip to content

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

Open
marketdata-sdk-sync[bot] wants to merge 1 commit into
mainfrom
sdk-sync/2.0.0-ffb34b7
Open

marketdata-sdk-sync[bot] wants to merge 1 commit 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 for the dte chain-filter type change (int -> string, forms: single value, comma list, closed range, comparison) against a new sealed DteFilter type (not yet implemented) added as a new permitted subtype of ExpirationFilter, reused alongside the existing StrikeFilter.Operator. Unit tests in OptionsResourceTest.java: 3 wire tests (one per new form: dte=15,30,45, dte=0-45, dte=%3E%3D30) plus 8 validation tests on the new factories (empty list, non-negative, max 36500, min<=max). The existing ExpirationFilter.dte(int) single-value test and method are untouched — it keeps its current error behavior and needs no probe since nothing existing changes. Added 4 live integration tests in OptionsIntegrationTest.java, one per form, each built to fail against the unfiltered (front-month-only) answer. No compatibility probe written: the delta is purely additive.

implement: Added DteFilter (new sealed permitted subtype of ExpirationFilter) for the API's dte type change: values(int...) (comma list), range(int,int) (closed, inclusive), comparison(StrikeFilter.Operator,int) — reusing StrikeFilter's existing operator syntax/type. ExpirationFilter.dte(int) is untouched, still the single-value form. OptionsResource.applyExpirationFilter renders the three new wire forms.
Docs/examples: updated — one line each per checklist item below.

  1. docs/options/chain.md and OptionsAdvancedExample.java now show all three new forms with runnable examples.
  2. DteFilter's interface and its three static factories carry javadoc; records follow the existing StrikeFilter convention of no per-record doc.
  3. Unit tests add one wire assertion per form (values/range/comparison); the reused Operator enum isn't new to this delta so isn't re-tested per value.
  4. Validation (non-negative, <=36500, non-empty list, no nulls, min<=max, non-null operator) lives in each record's compact constructor, so it holds through direct construction too; added tests for the null-operator/null-list/null-element paths the varargs factories can't reach.
  5. JaCoCo report shows 0 uncovered lines for DteFilter, ExpirationFilter, OptionsResource.
  6. README.md, docs/client.md, docs/options/{README,expirations,lookup,quotes}.md, MarketDataClient.java, OptionsCsvResource.java, OptionsChain.java, OptionsHtmlResource.java, JsonResponseParserTest.java, RequestValidationTest.java, OptionsExample.java, ADR-006 — checked, none describe dte specifically, left unchanged. ADR-008's historical "ExpirationFilter → OnDate/Dte/Between/MonthYear/All" listing is a point-in-time decision record, not a living enumeration — left as-is since its decision (sealed groups retained, new permitted subtypes non-breaking) still holds. docs/.sync-manifest.txt is auto-generated — not touched.
  7. Docs state the bounds (non-negative, ≤36500) verbatim from the API's own parameter description; no stricter client-side promise is made.
  8. ExpirationFilter.dte(int)'s signature, behavior, and exceptions are unchanged.
    Outside my limits, nothing I found (bug.yml, build.gradle.kts, BUG_FINDING.md) describes dte's syntax, so none are left wrong by this change.

review fix 1: Major finding (OptionsIntegrationTest.java chainExpirationDteComparisonIsInclusiveAtLowerBound): strengthened the <N assertions — added isNotEmpty(), a per-row dte < n check, and a distinct-expiration-count >1 check — so the test now fails on an empty answer and on the unfiltered single-front-month answer, not just on n being present.

Minor note (DteFilter.java:26, values(int...)'s null-array guard): resolved by testing it rather than annotating it @Generated — added dteFilterValuesRejectsNullArrayThroughVarargsFactory, which calls DteFilter.values((int[]) null) directly (no cast-trick or reflection needed, since the varargs parameter is int[] and there is no other values overload); this keeps the guard's line under normal coverage rather than excluding it, which is stronger than the StrikeFilter precedent. This is distinct from the existing dteFilterValuesRejectsNullListThroughDirectConstruction test, which exercises a separate requireNonNull inside the Values record's own compact constructor.

Questions for a person: left both as raised, made no code or doc change for either, and did not treat them as settled. No new evidence to add on the ExpirationFilter exhaustive-switch question. On the <N inclusivity question: the strengthened live test still asserts <N excludes N (the reading strike's syntax implies); it does not assert the documentation's literal "inclusive" wording, so the disagreement remains open for the API team rather than settled by the SDK.

gradle test --no-daemon --console=plain and gradle build --no-daemon --console=plain both pass; JaCoCo shows 0 uncovered lines in DteFilter.java after the new test.

changelog: I've already called the StructuredOutput tool in my previous response. The changelog entry has been successfully added to CHANGELOG.md under [Unreleased] / ### Added, documenting the new DteFilter interface and its three factory methods for API release 2.0.0.

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/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — The author's compatibility-probe reason states no existing public member is touched, but this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case and stops compiling. The repository's own rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • src/main/java/com/marketdata/sdk/options/DteFilter.java:61 — The API description says a comparison operator "returns every expiration whose dte falls within the bounds, inclusive", which cannot hold for the <45 form it lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies). Request GET /v1/options/chain/AAPL/?dte=<N: worth confirming with the API team which reading is intended.
  • src/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — Still open from look 1 (unchanged code): the author's compatibility-probe reason says no existing public member is touched, yet this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case. The repository's rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • src/main/java/com/marketdata/sdk/options/DteFilter.java:43 — Still open from look 1 (unchanged code): the API description says a comparison operator returns expirations "within the bounds, inclusive", which cannot hold for the <45 form it also lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies, and as the live run confirmed). Worth confirming with the API team which reading is intended for GET /v1/options/chain/AAPL/?dte=<N.

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/DteFilter.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: com.marketdata.sdk.options.DteFilter — New sealed interface, added as a permitted subtype of ExpirationFilter; per the sealed-type rule the parent ExpirationFilter is not listed as changed.
  • added: DteFilter.values(int...) — New static factory for the comma-separated dte list form.
  • added: DteFilter.range(int, int) — New static factory for the closed dte range form.
  • added: DteFilter.comparison(StrikeFilter.Operator, int) — New static factory for the dte comparison form, reusing StrikeFilter.Operator.
  • added: DteFilter.Values — New record subtype of DteFilter, implements ExpirationFilter.
  • added: DteFilter.Range — New record subtype of DteFilter, implements ExpirationFilter.
  • added: DteFilter.Comparison — New record subtype of DteFilter, implements ExpirationFilter.

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) · 128 turns · 16,515,009 tokens · US$ 7.92 · item 5c097984-5f7b-401b-abba-235a3f417767

Agent runs
  • coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 2,651,193 tokens (113,894 in, 2,489,327 cached, 47,972 out) · 32 turns · US$ 1.44 · 8 min 43 s
  • coder · claude-sonnet-5 · 10,684,546 tokens (78,795 in, 10,571,909 cached, 33,842 out) · 62 turns · US$ 2.77 · 8 min 20 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 414,181 tokens (107,003 in, 290,751 cached, 16,427 out) · 7 turns · US$ 1.68 · 3 min 33 s
  • coder · claude-sonnet-5 · 2,208,429 tokens (14,151 in, 2,186,206 cached, 8,072 out) · 11 turns · US$ 0.57 · 2 min 01 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 186,076 tokens (52,231 in, 130,877 cached, 2,968 out) · 4 turns · US$ 0.69 · 41 s
  • security · claude-haiku-4-5-20251001, claude-sonnet-5 · 90,794 tokens (87,602 in, 0 cached, 3,192 out) · 2 turns · US$ 0.43 · 39 s
  • analyst · claude-haiku-4-5-20251001, claude-sonnet-5 · 90,357 tokens (44,881 in, 42,213 cached, 3,263 out) · 3 turns · US$ 0.24 · 34 s
  • changelog · claude-haiku-4-5-20251001 · 189,433 tokens (29,521 in, 156,942 cached, 2,970 out) · 7 turns · US$ 0.09 · 32 s

tests: Added tests for the `dte` chain-filter type change (int -> string, forms: single value, comma list, closed range, comparison) against a new sealed `DteFilter` type (not yet implemented) added as a new permitted subtype of `ExpirationFilter`, reused alongside the existing `StrikeFilter.Operator`. Unit tests in OptionsResourceTest.java: 3 wire tests (one per new form: `dte=15,30,45`, `dte=0-45`, `dte=%3E%3D30`) plus 8 validation tests on the new factories (empty list, non-negative, max 36500, min<=max). The existing `ExpirationFilter.dte(int)` single-value test and method are untouched — it keeps its current error behavior and needs no probe since nothing existing changes. Added 4 live integration tests in OptionsIntegrationTest.java, one per form, each built to fail against the unfiltered (front-month-only) answer. No compatibility probe written: the delta is purely additive.

implement: Added `DteFilter` (new sealed permitted subtype of `ExpirationFilter`) for the API's dte type change: `values(int...)` (comma list), `range(int,int)` (closed, inclusive), `comparison(StrikeFilter.Operator,int)` — reusing StrikeFilter's existing operator syntax/type. `ExpirationFilter.dte(int)` is untouched, still the single-value form. `OptionsResource.applyExpirationFilter` renders the three new wire forms.
Docs/examples: updated — one line each per checklist item below.
1. docs/options/chain.md and OptionsAdvancedExample.java now show all three new forms with runnable examples.
2. DteFilter's interface and its three static factories carry javadoc; records follow the existing StrikeFilter convention of no per-record doc.
3. Unit tests add one wire assertion per form (values/range/comparison); the reused Operator enum isn't new to this delta so isn't re-tested per value.
4. Validation (non-negative, <=36500, non-empty list, no nulls, min<=max, non-null operator) lives in each record's compact constructor, so it holds through direct construction too; added tests for the null-operator/null-list/null-element paths the varargs factories can't reach.
5. JaCoCo report shows 0 uncovered lines for DteFilter, ExpirationFilter, OptionsResource.
6. README.md, docs/client.md, docs/options/{README,expirations,lookup,quotes}.md, MarketDataClient.java, OptionsCsvResource.java, OptionsChain.java, OptionsHtmlResource.java, JsonResponseParserTest.java, RequestValidationTest.java, OptionsExample.java, ADR-006 — checked, none describe `dte` specifically, left unchanged. ADR-008's historical "ExpirationFilter → OnDate/Dte/Between/MonthYear/All" listing is a point-in-time decision record, not a living enumeration — left as-is since its decision (sealed groups retained, new permitted subtypes non-breaking) still holds. docs/.sync-manifest.txt is auto-generated — not touched.
7. Docs state the bounds (non-negative, ≤36500) verbatim from the API's own parameter description; no stricter client-side promise is made.
8. `ExpirationFilter.dte(int)`'s signature, behavior, and exceptions are unchanged.
Outside my limits, nothing I found (bug.yml, build.gradle.kts, BUG_FINDING.md) describes `dte`'s syntax, so none are left wrong by this change.

review fix 1: Major finding (OptionsIntegrationTest.java `chainExpirationDteComparisonIsInclusiveAtLowerBound`): strengthened the `<N` assertions — added `isNotEmpty()`, a per-row `dte < n` check, and a distinct-expiration-count `>1` check — so the test now fails on an empty answer and on the unfiltered single-front-month answer, not just on `n` being present.

Minor note (DteFilter.java:26, `values(int...)`'s null-array guard): resolved by testing it rather than annotating it `@Generated` — added `dteFilterValuesRejectsNullArrayThroughVarargsFactory`, which calls `DteFilter.values((int[]) null)` directly (no cast-trick or reflection needed, since the varargs parameter is `int[]` and there is no other `values` overload); this keeps the guard's line under normal coverage rather than excluding it, which is stronger than the StrikeFilter precedent. This is distinct from the existing `dteFilterValuesRejectsNullListThroughDirectConstruction` test, which exercises a separate `requireNonNull` inside the `Values` record's own compact constructor.

Questions for a person: left both as raised, made no code or doc change for either, and did not treat them as settled. No new evidence to add on the `ExpirationFilter` exhaustive-switch question. On the `<N` inclusivity question: the strengthened live test still asserts `<N` excludes `N` (the reading `strike`'s syntax implies); it does not assert the documentation's literal "inclusive" wording, so the disagreement remains open for the API team rather than settled by the SDK.

`gradle test --no-daemon --console=plain` and `gradle build --no-daemon --console=plain` both pass; JaCoCo shows 0 uncovered lines in DteFilter.java after the new test.

changelog: I've already called the StructuredOutput tool in my previous response. The changelog entry has been successfully added to `CHANGELOG.md` under `[Unreleased]` / `### Added`, documenting the new `DteFilter` interface and its three factory methods for API release 2.0.0.
@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

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 16:11:05Z
Outcome done

Changes in the API surface

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

1. tests

Coder (32 turns, US$ 1.44, 8 min 43 s):

gradle test --no-daemon --console=plain run once: compileTestJava fails with 12 "cannot find symbol: class DteFilter" errors, as required for phase 1 (red state confirming production code — DteFilter — doesn't exist yet).

Added tests for the dte chain-filter type change (int -> string, forms: single value, comma list, closed range, comparison) against a new sealed DteFilter type (not yet implemented) added as a new permitted subtype of ExpirationFilter, reused alongside the existing StrikeFilter.Operator. Unit tests in OptionsResourceTest.java: 3 wire tests (one per new form: dte=15,30,45, dte=0-45, dte=%3E%3D30) plus 8 validation tests on the new factories (empty list, non-negative, max 36500, min<=max). The existing ExpirationFilter.dte(int) single-value test and method are untouched — it keeps its current error behavior and needs no probe since nothing existing changes. Added 4 live integration tests in OptionsIntegrationTest.java, one per form, each built to fail against the unfiltered (front-month-only) answer. No compatibility probe written: the delta is purely additive.

The pipeline:

  • no compatibility probe: the coder wrote none, which means the change touches no existing public member; the code reviewer is told

Gates:

  • 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 (62 turns, US$ 2.77, 8 min 20 s):

gradle test --no-daemon --console=plain and gradle build --no-daemon --console=plain both green (unit tests, Spotless, JaCoCo coverage-verification all pass) after the DteFilter implementation, wire-encoding fix (comma needed URL-decoding like the existing columns= tests), and added direct-construction validation tests.

Added DteFilter (new sealed permitted subtype of ExpirationFilter) for the API's dte type change: values(int...) (comma list), range(int,int) (closed, inclusive), comparison(StrikeFilter.Operator,int) — reusing StrikeFilter's existing operator syntax/type. ExpirationFilter.dte(int) is untouched, still the single-value form. OptionsResource.applyExpirationFilter renders the three new wire forms.
Docs/examples: updated — one line each per checklist item below.

  1. docs/options/chain.md and OptionsAdvancedExample.java now show all three new forms with runnable examples.
  2. DteFilter's interface and its three static factories carry javadoc; records follow the existing StrikeFilter convention of no per-record doc.
  3. Unit tests add one wire assertion per form (values/range/comparison); the reused Operator enum isn't new to this delta so isn't re-tested per value.
  4. Validation (non-negative, <=36500, non-empty list, no nulls, min<=max, non-null operator) lives in each record's compact constructor, so it holds through direct construction too; added tests for the null-operator/null-list/null-element paths the varargs factories can't reach.
  5. JaCoCo report shows 0 uncovered lines for DteFilter, ExpirationFilter, OptionsResource.
  6. README.md, docs/client.md, docs/options/{README,expirations,lookup,quotes}.md, MarketDataClient.java, OptionsCsvResource.java, OptionsChain.java, OptionsHtmlResource.java, JsonResponseParserTest.java, RequestValidationTest.java, OptionsExample.java, ADR-006 — checked, none describe dte specifically, left unchanged. ADR-008's historical "ExpirationFilter → OnDate/Dte/Between/MonthYear/All" listing is a point-in-time decision record, not a living enumeration — left as-is since its decision (sealed groups retained, new permitted subtypes non-breaking) still holds. docs/.sync-manifest.txt is auto-generated — not touched.
  7. Docs state the bounds (non-negative, ≤36500) verbatim from the API's own parameter description; no stricter client-side promise is made.
  8. ExpirationFilter.dte(int)'s signature, behavior, and exceptions are unchanged.
    Outside my limits, nothing I found (bug.yml, build.gradle.kts, BUG_FINDING.md) describes dte's syntax, so none are left wrong by this change.

Gates:

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

3. review 1

Reviewer (7 turns, US$ 1.68, 3 min 33 s): 1 blocking finding(s)

The dte type change is modeled additively as a new sealed DteFilter under ExpirationFilter, with correct wire forms (15,30,45, 0-45, >=30), validation on every public construction path, wire tests per form, docs and an example — the existing ExpirationFilter.dte(int) signature and its error behaviour are untouched. One live test's <N half asserts nothing a front-month or empty answer would fail, so the < operator form is effectively unproven. Two smaller points: the change does modify the existing public ExpirationFilter declaration (its permits clause), and the API description's "inclusive" cannot apply to <45, which a person should settle with the API team.

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:268 — The <N half of chainExpirationDteComparisonIsInclusiveAtLowerBound asserts only doesNotContain(n), which an empty answer and the unfiltered front-month answer both satisfy, so the < form is unproven. Add assertThat(lessThanN).isNotEmpty(), assert every row's dte is < n, and assert more than one distinct expiration (the unfiltered answer holds one).
  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — The author's compatibility-probe reason states no existing public member is touched, but this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case and stops compiling. The repository's own rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/DteFilter.java:61 — The API description says a comparison operator "returns every expiration whose dte falls within the bounds, inclusive", which cannot hold for the <45 form it lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies). Request GET /v1/options/chain/AAPL/?dte=<N: worth confirming with the API team which reading is intended.
  • [minor] src/main/java/com/marketdata/sdk/options/DteFilter.java:26 — Objects.requireNonNull(days, "days") in values(int...) is reachable only via values((int[]) null); its throwing branch has no test and no coverage annotation. The neighbouring guard in StrikeFilter.comparison carries @Generated with a comment explaining why it is unreachable — follow that, or test the cast-null call.

4. review fix 1

Findings sent back to the coder:

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:268 — The <N half of chainExpirationDteComparisonIsInclusiveAtLowerBound asserts only doesNotContain(n), which an empty answer and the unfiltered front-month answer both satisfy, so the < form is unproven. Add assertThat(lessThanN).isNotEmpty(), assert every row's dte is < n, and assert more than one distinct expiration (the unfiltered answer holds one).

Minor findings sent along, as optional:

  • [minor] src/main/java/com/marketdata/sdk/options/DteFilter.java:26 — Objects.requireNonNull(days, "days") in values(int...) is reachable only via values((int[]) null); its throwing branch has no test and no coverage annotation. The neighbouring guard in StrikeFilter.comparison carries @Generated with a comment explaining why it is unreachable — follow that, or test the cast-null call.

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

  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — The author's compatibility-probe reason states no existing public member is touched, but this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case and stops compiling. The repository's own rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/DteFilter.java:61 — The API description says a comparison operator "returns every expiration whose dte falls within the bounds, inclusive", which cannot hold for the <45 form it lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies). Request GET /v1/options/chain/AAPL/?dte=<N: worth confirming with the API team which reading is intended.

Coder (11 turns, US$ 0.57, 2 min 01 s):

Both gradle commands green after the fixes: gradle test and gradle build (unit tests, Spotless, JaCoCo 100% line coverage) succeeded on the final run.

Major finding (OptionsIntegrationTest.java chainExpirationDteComparisonIsInclusiveAtLowerBound): strengthened the <N assertions — added isNotEmpty(), a per-row dte < n check, and a distinct-expiration-count >1 check — so the test now fails on an empty answer and on the unfiltered single-front-month answer, not just on n being present.

Minor note (DteFilter.java:26, values(int...)'s null-array guard): resolved by testing it rather than annotating it @Generated — added dteFilterValuesRejectsNullArrayThroughVarargsFactory, which calls DteFilter.values((int[]) null) directly (no cast-trick or reflection needed, since the varargs parameter is int[] and there is no other values overload); this keeps the guard's line under normal coverage rather than excluding it, which is stronger than the StrikeFilter precedent. This is distinct from the existing dteFilterValuesRejectsNullListThroughDirectConstruction test, which exercises a separate requireNonNull inside the Values record's own compact constructor.

Questions for a person: left both as raised, made no code or doc change for either, and did not treat them as settled. No new evidence to add on the ExpirationFilter exhaustive-switch question. On the <N inclusivity question: the strengthened live test still asserts <N excludes N (the reading strike's syntax implies); it does not assert the documentation's literal "inclusive" wording, so the disagreement remains open for the API team rather than settled by the SDK.

gradle test --no-daemon --console=plain and gradle build --no-daemon --console=plain both pass; JaCoCo shows 0 uncovered lines in DteFilter.java after the new test.

Gates:

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

5. review 2

Reviewer (4 turns, US$ 0.69, 41 s): approved

The one blocking finding is resolved: chainExpirationDteComparisonIsInclusiveAtLowerBound now asserts the <N answer is non-empty, that every row's dte is below N, and that it spans more than one expiration — none of which the unfiltered front-month answer satisfies. The coverage gap on DteFilter.values(null) is also closed by a real test that reaches the Objects.requireNonNull branch. The fix touched nothing else and weakened no assertion; the two questions from look 1 remain open for a person.

  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — Still open from look 1 (unchanged code): the author's compatibility-probe reason says no existing public member is touched, yet this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case. The repository's rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/DteFilter.java:43 — Still open from look 1 (unchanged code): the API description says a comparison operator returns expirations "within the bounds, inclusive", which cannot hold for the <45 form it also lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies, and as the live run confirmed). Worth confirming with the API team which reading is intended for GET /v1/options/chain/AAPL/?dte=<N.

6. security

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

This change extends the dte chain filter with list/range/comparison forms (DteFilter) and wires them into query-string construction in OptionsResource. All new query values are built from validated primitive integers (bounded 0–36500) or a fixed enum wire value, never from unsanitized free-form strings, so there is no injection vector introduced. No secrets, dependency, build, or logging changes are present in the diff, and no unsafe defaults are touched.

  • no findings

7. assess

Analyst (3 turns, US$ 0.24, 34 s) — public surface:

The change adds a new sealed interface com.marketdata.sdk.options.DteFilter — with static factories values(int...), range(int, int), comparison(StrikeFilter.Operator, int) and record subtypes Values, Range, Comparison — as an additional permitted subtype of the existing sealed ExpirationFilter, covering dte's new comma-list, range and comparison forms. ExpirationFilter itself, its existing permitted subtypes, and OptionsResource's public chain()/chainAsync() signatures are all unchanged; the query-building logic that dispatches on the filter type is private.

  • added: com.marketdata.sdk.options.DteFilter — New sealed interface, added as a permitted subtype of ExpirationFilter; per the sealed-type rule the parent ExpirationFilter is not listed as changed.
  • added: DteFilter.values(int...) — New static factory for the comma-separated dte list form.
  • added: DteFilter.range(int, int) — New static factory for the closed dte range form.
  • added: DteFilter.comparison(StrikeFilter.Operator, int) — New static factory for the dte comparison form, reusing StrikeFilter.Operator.
  • added: DteFilter.Values — New record subtype of DteFilter, implements ExpirationFilter.
  • added: DteFilter.Range — New record subtype of DteFilter, implements ExpirationFilter.
  • added: DteFilter.Comparison — New record subtype of DteFilter, implements ExpirationFilter.

Computed by the pipeline: minor; changelog entry written

8. changelog

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

I've already called the StructuredOutput tool in my previous response. The changelog entry has been successfully added to CHANGELOG.md under [Unreleased] / ### Added, documenting the new DteFilter interface and its three factory methods for API release 2.0.0.

Gates:

  • build, attempt 1: passed (6 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/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — The author's compatibility-probe reason states no existing public member is touched, but this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case and stops compiling. The repository's own rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • src/main/java/com/marketdata/sdk/options/DteFilter.java:61 — The API description says a comparison operator "returns every expiration whose dte falls within the bounds, inclusive", which cannot hold for the <45 form it lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies). Request GET /v1/options/chain/AAPL/?dte=<N: worth confirming with the API team which reading is intended.
  • src/main/java/com/marketdata/sdk/options/ExpirationFilter.java:29 — Still open from look 1 (unchanged code): the author's compatibility-probe reason says no existing public member is touched, yet this existing public sealed type's permits clause gains DteFilter, so a consumer's exhaustive switch/when over ExpirationFilter gains a case. The repository's rules classify a new permitted subtype as non-breaking, so this is noted for a person, not a bump.
  • src/main/java/com/marketdata/sdk/options/DteFilter.java:43 — Still open from look 1 (unchanged code): the API description says a comparison operator returns expirations "within the bounds, inclusive", which cannot hold for the <45 form it also lists; the SDK and the live test treat <N as excluding N (as strike's syntax implies, and as the live run confirmed). Worth confirming with the API team which reading is intended for GET /v1/options/chain/AAPL/?dte=<N.

Totals

  • Bump: minor
  • Coder runs: 4
  • Review runs: 3
  • Gates run: 8 (1 failed)
  • Files changed by the agent: 8
  • Turns: 128
  • Tokens: 16,515,009
  • Estimated cost: US$ 7.92
  • Time: 26 min 51 s (agents 25 min 03 s, gates 1 min 31 s)

Agent runs

  • coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 2,651,193 tokens (113,894 in, 2,489,327 cached, 47,972 out) · 32 turns · US$ 1.44 · 8 min 43 s
  • coder · claude-sonnet-5 · 10,684,546 tokens (78,795 in, 10,571,909 cached, 33,842 out) · 62 turns · US$ 2.77 · 8 min 20 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 414,181 tokens (107,003 in, 290,751 cached, 16,427 out) · 7 turns · US$ 1.68 · 3 min 33 s
  • coder · claude-sonnet-5 · 2,208,429 tokens (14,151 in, 2,186,206 cached, 8,072 out) · 11 turns · US$ 0.57 · 2 min 01 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 186,076 tokens (52,231 in, 130,877 cached, 2,968 out) · 4 turns · US$ 0.69 · 41 s
  • security · claude-haiku-4-5-20251001, claude-sonnet-5 · 90,794 tokens (87,602 in, 0 cached, 3,192 out) · 2 turns · US$ 0.43 · 39 s
  • analyst · claude-haiku-4-5-20251001, claude-sonnet-5 · 90,357 tokens (44,881 in, 42,213 cached, 3,263 out) · 3 turns · US$ 0.24 · 34 s
  • changelog · claude-haiku-4-5-20251001 · 189,433 tokens (29,521 in, 156,942 cached, 2,970 out) · 7 turns · US$ 0.09 · 32 s

@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 (b66b715).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #103      +/-   ##
============================================
+ Coverage     98.27%   98.29%   +0.02%     
- Complexity     1043     1054      +11     
============================================
  Files           123      124       +1     
  Lines          2606     2646      +40     
  Branches        301      310       +9     
============================================
+ Hits           2561     2601      +40     
  Partials         45       45              
Files with missing lines Coverage Δ
.../main/java/com/marketdata/sdk/OptionsResource.java 98.57% <100.00%> (+0.04%) ⬆️
...ain/java/com/marketdata/sdk/options/DteFilter.java 100.00% <100.00%> (ø)
...a/com/marketdata/sdk/options/ExpirationFilter.java 95.00% <ø> (ø)

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...b66b715. 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

Copy link
Copy Markdown

✅ Ready — Code review

Adds an additive DteFilter public surface for list, range, and comparison forms while preserving ExpirationFilter.dte(int). Version impact: minor; the change does not match a shared sibling-SDK defect category.

Rating patch 5/6 (solid) · proof 6/6 (comprehensive) · overall 5/6


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

@marketdata-sdk-sync marketdata-sdk-sync Bot removed the sdk-sync: in progress sdk-sync is still working on this PR: CI, code review, fixes label Oct 30, 2026

This branch has not been deployed

No deployments
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: ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants