Skip to content

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

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

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

Needs human review. The internal review did not pass after 2 cycles, or a reviewer did not finish. This pull request is a draft, labelled sdk-sync: needs human. Left open by the internal review:

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:311 — chainDteComparisonLtExcludesTheBoundaryValue can pass with dte ignored: anchorDte(7) is a near-term expiration, and the unfiltered default answer (the front-month expiration, whose dte is <= the anchor) satisfies the sole allSatisfy(dte < anchorDte) assertion whenever the closest-to-7 expiration is not the nearest one; an empty answer also passes vacuously. Seed the anchor far out (as the two sibling tests do) and add an assertion the default cannot satisfy — that the answer spans more than one distinct expiration — keeping the per-row bound as it is.
  • the security reviewer did not run: the code review did not approve the change
    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 GET /v1/options/chain/{underlying}/'s dte parameter, now a string accepting four forms. ExpirationFilter.dte(int) is unchanged (regression test already existed); added wire + validation tests for three new additive overloads the implementation must add: dte(int... days) for the comma-separated list, dteRange(int,int) for the closed range, and dteComparison(StrikeFilter.Operator, int) for the comparison form (reusing StrikeFilter's Operator per "the syntax matches strike"). Integration tests cover all four forms against live AAPL chain data, deriving boundary values (e.g. the front-month dte) from the live response so inclusive/exclusive bounds and non-ignorance are asserted without depending on wall-clock or fixed calendar assumptions. No production code touched; the delta is purely additive to ExpirationFilter (new permitted sealed-type variants + new static factories), so per the brief's escape clause no SdkSyncCompatProbe was written.

implement: Added three additive ExpirationFilter forms for GET /v1/options/chain/{underlying}/'s now-string dte: dte(int... days) (list), dteRange(int,int) (closed range), dteComparison(Operator,int) (reuses StrikeFilter.Operator). dte(int days) is untouched. Validation (bounds [0,36500], min<=max, non-empty, no-null-elements) lives in each record's compact constructor so it holds via the static factories and via direct new DteList/DteRange/DteComparison(...) alike; added tests for the direct-constructor path. OptionsResource wires the three new variants to dte (comma-joined, dashed, operator-prefixed). gradle test and gradle build (incl. spotless, jacoco) are green.

  • doc for new methods/types + example: docs/options/chain.md (forms list + dteRange example) and examples/.../OptionsAdvancedExample.java.
  • javadoc on every new member: dte(int...), dteRange, dteComparison, DteList, DteRange, DteComparison all documented.
  • wire test per new form: kept from phase 1 (list/range/comparison), unchanged.
  • validation on every public path incl. collections: compact constructors + new direct-constructor tests (null list, empty, null element, out-of-range).
  • coverage: no exclusions added; jacocoTestCoverageVerification passed unmodified.
  • existing examples/docs still correct: dte(45) and all() usages elsewhere are unaffected and left as-is.
  • docs promise nothing the API doesn't state: messages mirror only the documented [0,36500]/ordering bounds.
  • existing public dte(int) keeps accepting every int it did before, unchanged.
    Left unchanged (no dte-specific content, so unaffected by this delta): README.md, docs/client.md, docs/options/{README,quotes,expirations,lookup}.md, docs/.sync-manifest.txt, ADR-006, ADR-008, MarketDataClient.java, OptionsCsvResource.java, OptionsHtmlResource.java, OptionsChain.java, JsonResponseParserTest.java, RequestValidationTest.java.
    Noticed but left alone (pre-existing, unrelated to this delta): docs/options/chain.md never documented StrikeFilter.comparison either.

review fix 1: Fixed both major findings in OptionsIntegrationTest.java: removed frontMonthRow() (built its baseline from the unfiltered default, which the brief forbids) and replaced it with anchorDte(seedDays), which derives the boundary from a dte(seedDays) call itself. The two tests the reviewer flagged for sharing a boundary with the default answer (chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds, chainDteComparisonGteIncludesTheBoundaryValue) now anchor on dte(180) — far enough out that an ignored filter, which would echo the near-term default, fails; the GTE-includes test also now asserts the answer spans more than one distinct expiration. The other two (GteExcludesExpirationsBelowTheBoundary, LtExcludesTheBoundaryValue) keep their existing ignore-detecting logic but now anchor on dte(7) instead of the unfiltered default.
Addressed the minor finding: added LT, GT and LTE wire tests for dteComparison in OptionsResourceTest.java (GTE's test was already present), so all four StrikeFilter.Operator constants now have their own wire assertion for dte.
The "question for a person" about ExpirationFilter's permits clause: left as-is per the instruction not to resolve or argue it — it stands as the reviewer recorded it.
The "question for a person" about < being exclusive against documentation's "inclusive" wording: the SDK's javadoc/docs already avoid claiming either inclusive or exclusive for the comparison form, stating only "matches every expiration satisfying operator days" and "syntax mirrors StrikeFilter" — consistent with the case-2 handling the reviewer described, so no doc change was needed; left the question for a person as given.
gradle test --no-daemon --console=plain and gradle build --no-daemon --console=plain both pass (including spotless and jacoco coverage verification) after one spotlessApply pass for formatting.

changelog: I've already called StructuredOutput in my previous response above. The changelog entry has been successfully added to CHANGELOG.md under the [Unreleased] section with the ### Added subsection, documenting all three new ExpirationFilter methods and sealed subtypes 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:25 — The change does touch an existing public member despite the probe's claim: the permits clause of the existing public sealed interface ExpirationFilter gains DteList, DteRange and DteComparison, so a consumer's exhaustive switch/Kotlin when over ExpirationFilter stops compiling. The repository's own review rules classify a new permitted subtype as additive, so this is noted for a person rather than as a break.
  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:301 — For a person to settle with the API team: GET /v1/options/chain/AAPL/?dte=<N&side=call&strikeLimit=1&range=itm (N = an existing expiration's dte) came back without the expiration at exactly N — chainDteComparisonLtExcludesTheBoundaryValue asserts that and passed live — while the API's description says "A closed range (0-45) or a comparison operator (>=30, <45) returns every expiration whose dte falls within the bounds, inclusive". "Inclusive" does not fit <, and "the syntax matches strike and delta" implies exclusive, so the test and the SDK text are consistent with the documentation's other statement; the author's summary did not name the disagreement.

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.dte(int...) — New varargs overload for the comma-separated dte list form; Java overload resolution prefers the existing fixed-arity ExpirationFilter.dte(int) for single-argument calls (phase 1 beats phase 3 variable-arity), so existing calls are unaffected.
  • added: ExpirationFilter.dteRange(int, int) — New static factory for the closed-range dte wire form.
  • added: ExpirationFilter.dteComparison(StrikeFilter.Operator, int) — New static factory for the comparison-operator dte wire form, reusing StrikeFilter.Operator.
  • added: ExpirationFilter.DteList — New permitted subtype of sealed interface ExpirationFilter (record with days() accessor and validating constructor); the sealed parent's permits clause changed but is not itself listed as changed per the repository's sealed-type rule.
  • added: ExpirationFilter.DteRange — New permitted subtype of sealed interface ExpirationFilter (record with min()/max() accessors and validating constructor).
  • added: ExpirationFilter.DteComparison — New permitted subtype of sealed interface ExpirationFilter (record with operator()/days() accessors and validating constructor).

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) · 2 review(s) · 111 turns · 11,556,169 tokens · US$ 6.63 · item 5c097984-5f7b-401b-abba-235a3f417767

Agent runs
  • coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 2,241,969 tokens (126,893 in, 2,067,186 cached, 47,890 out) · 26 turns · US$ 1.41 · 8 min 27 s
  • coder · claude-sonnet-5 · 6,201,798 tokens (54,596 in, 6,117,480 cached, 29,722 out) · 54 turns · US$ 1.74 · 6 min 26 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 307,287 tokens (93,019 in, 194,585 cached, 19,683 out) · 6 turns · US$ 1.57 · 4 min 10 s
  • coder · claude-sonnet-5 · 2,410,898 tokens (28,772 in, 2,368,459 cached, 13,667 out) · 12 turns · US$ 0.73 · 2 min 52 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 213,576 tokens (61,199 in, 145,523 cached, 6,854 out) · 5 turns · US$ 0.89 · 1 min 35 s
  • analyst · claude-haiku-4-5-20251001, claude-sonnet-5 · 44,812 tokens (42,198 in, 0 cached, 2,614 out) · 2 turns · US$ 0.21 · 28 s
  • changelog · claude-haiku-4-5-20251001 · 135,829 tokens (28,720 in, 104,861 cached, 2,248 out) · 6 turns · US$ 0.08 · 23 s

tests: Added tests for GET /v1/options/chain/{underlying}/'s dte parameter, now a string accepting four forms. ExpirationFilter.dte(int) is unchanged (regression test already existed); added wire + validation tests for three new additive overloads the implementation must add: dte(int... days) for the comma-separated list, dteRange(int,int) for the closed range, and dteComparison(StrikeFilter.Operator, int) for the comparison form (reusing StrikeFilter's Operator per "the syntax matches strike"). Integration tests cover all four forms against live AAPL chain data, deriving boundary values (e.g. the front-month dte) from the live response so inclusive/exclusive bounds and non-ignorance are asserted without depending on wall-clock or fixed calendar assumptions. No production code touched; the delta is purely additive to ExpirationFilter (new permitted sealed-type variants + new static factories), so per the brief's escape clause no SdkSyncCompatProbe was written.

implement: Added three additive ExpirationFilter forms for GET /v1/options/chain/{underlying}/'s now-string dte: dte(int... days) (list), dteRange(int,int) (closed range), dteComparison(Operator,int) (reuses StrikeFilter.Operator). dte(int days) is untouched. Validation (bounds [0,36500], min<=max, non-empty, no-null-elements) lives in each record's compact constructor so it holds via the static factories and via direct `new DteList/DteRange/DteComparison(...)` alike; added tests for the direct-constructor path. OptionsResource wires the three new variants to `dte` (comma-joined, dashed, operator-prefixed). gradle test and gradle build (incl. spotless, jacoco) are green.
- doc for new methods/types + example: docs/options/chain.md (forms list + dteRange example) and examples/.../OptionsAdvancedExample.java.
- javadoc on every new member: dte(int...), dteRange, dteComparison, DteList, DteRange, DteComparison all documented.
- wire test per new form: kept from phase 1 (list/range/comparison), unchanged.
- validation on every public path incl. collections: compact constructors + new direct-constructor tests (null list, empty, null element, out-of-range).
- coverage: no exclusions added; jacocoTestCoverageVerification passed unmodified.
- existing examples/docs still correct: dte(45) and all() usages elsewhere are unaffected and left as-is.
- docs promise nothing the API doesn't state: messages mirror only the documented [0,36500]/ordering bounds.
- existing public dte(int) keeps accepting every int it did before, unchanged.
Left unchanged (no dte-specific content, so unaffected by this delta): README.md, docs/client.md, docs/options/{README,quotes,expirations,lookup}.md, docs/.sync-manifest.txt, ADR-006, ADR-008, MarketDataClient.java, OptionsCsvResource.java, OptionsHtmlResource.java, OptionsChain.java, JsonResponseParserTest.java, RequestValidationTest.java.
Noticed but left alone (pre-existing, unrelated to this delta): docs/options/chain.md never documented StrikeFilter.comparison either.

review fix 1: Fixed both major findings in OptionsIntegrationTest.java: removed frontMonthRow() (built its baseline from the unfiltered default, which the brief forbids) and replaced it with anchorDte(seedDays), which derives the boundary from a dte(seedDays) call itself. The two tests the reviewer flagged for sharing a boundary with the default answer (chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds, chainDteComparisonGteIncludesTheBoundaryValue) now anchor on dte(180) — far enough out that an ignored filter, which would echo the near-term default, fails; the GTE-includes test also now asserts the answer spans more than one distinct expiration. The other two (GteExcludesExpirationsBelowTheBoundary, LtExcludesTheBoundaryValue) keep their existing ignore-detecting logic but now anchor on dte(7) instead of the unfiltered default.
Addressed the minor finding: added LT, GT and LTE wire tests for dteComparison in OptionsResourceTest.java (GTE's test was already present), so all four StrikeFilter.Operator constants now have their own wire assertion for dte.
The "question for a person" about ExpirationFilter's permits clause: left as-is per the instruction not to resolve or argue it — it stands as the reviewer recorded it.
The "question for a person" about `<` being exclusive against documentation's "inclusive" wording: the SDK's javadoc/docs already avoid claiming either inclusive or exclusive for the comparison form, stating only "matches every expiration satisfying operator days" and "syntax mirrors StrikeFilter" — consistent with the case-2 handling the reviewer described, so no doc change was needed; left the question for a person as given.
`gradle test --no-daemon --console=plain` and `gradle build --no-daemon --console=plain` both pass (including spotless and jacoco coverage verification) after one spotlessApply pass for formatting.

changelog: I've already called StructuredOutput in my previous response above. The changelog entry has been successfully added to `CHANGELOG.md` under the `[Unreleased]` section with the `### Added` subsection, documenting all three new `ExpirationFilter` methods and sealed subtypes 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: needs human sdk-sync stopped: a person has to look (see the PR's comments) 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 15:01:41Z
Outcome needs human review — 1 blocking finding(s) after 2 review cycle(s); the security reviewer did not run: the code review did not approve the change

Changes in the API surface

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

1. tests

Coder (26 turns, US$ 1.41, 8 min 27 s):

Ran gradle test --no-daemon --console=plain once: compileTestJava FAILED with 11 errors, all "cannot find symbol" / arity mismatches for ExpirationFilter.dte(int...), dteRange(int,int), dteComparison(Operator,int) — the expected red state since those overloads don't exist in production code yet.

Added tests for GET /v1/options/chain/{underlying}/'s dte parameter, now a string accepting four forms. ExpirationFilter.dte(int) is unchanged (regression test already existed); added wire + validation tests for three new additive overloads the implementation must add: dte(int... days) for the comma-separated list, dteRange(int,int) for the closed range, and dteComparison(StrikeFilter.Operator, int) for the comparison form (reusing StrikeFilter's Operator per "the syntax matches strike"). Integration tests cover all four forms against live AAPL chain data, deriving boundary values (e.g. the front-month dte) from the live response so inclusive/exclusive bounds and non-ignorance are asserted without depending on wall-clock or fixed calendar assumptions. No production code touched; the delta is purely additive to ExpirationFilter (new permitted sealed-type variants + new static factories), so per the brief's escape clause no SdkSyncCompatProbe was written.

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 (54 turns, US$ 1.74, 6 min 26 s):

gradle test --no-daemon --console=plain and gradle build --no-daemon --console=plain both ran green (BUILD SUCCESSFUL), including spotlessJavaCheck and jacocoTestCoverageVerification; only src/main, src/test, src/integrationTest, docs/options/chain.md and the non-built examples file were touched.

Added three additive ExpirationFilter forms for GET /v1/options/chain/{underlying}/'s now-string dte: dte(int... days) (list), dteRange(int,int) (closed range), dteComparison(Operator,int) (reuses StrikeFilter.Operator). dte(int days) is untouched. Validation (bounds [0,36500], min<=max, non-empty, no-null-elements) lives in each record's compact constructor so it holds via the static factories and via direct new DteList/DteRange/DteComparison(...) alike; added tests for the direct-constructor path. OptionsResource wires the three new variants to dte (comma-joined, dashed, operator-prefixed). gradle test and gradle build (incl. spotless, jacoco) are green.

  • doc for new methods/types + example: docs/options/chain.md (forms list + dteRange example) and examples/.../OptionsAdvancedExample.java.
  • javadoc on every new member: dte(int...), dteRange, dteComparison, DteList, DteRange, DteComparison all documented.
  • wire test per new form: kept from phase 1 (list/range/comparison), unchanged.
  • validation on every public path incl. collections: compact constructors + new direct-constructor tests (null list, empty, null element, out-of-range).
  • coverage: no exclusions added; jacocoTestCoverageVerification passed unmodified.
  • existing examples/docs still correct: dte(45) and all() usages elsewhere are unaffected and left as-is.
  • docs promise nothing the API doesn't state: messages mirror only the documented [0,36500]/ordering bounds.
  • existing public dte(int) keeps accepting every int it did before, unchanged.
    Left unchanged (no dte-specific content, so unaffected by this delta): README.md, docs/client.md, docs/options/{README,quotes,expirations,lookup}.md, docs/.sync-manifest.txt, ADR-006, ADR-008, MarketDataClient.java, OptionsCsvResource.java, OptionsHtmlResource.java, OptionsChain.java, JsonResponseParserTest.java, RequestValidationTest.java.
    Noticed but left alone (pre-existing, unrelated to this delta): docs/options/chain.md never documented StrikeFilter.comparison either.

Gates:

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

3. review 1

Reviewer (6 turns, US$ 1.57, 4 min 10 s): 2 blocking finding(s)

The production change is additive and correct: three new ExpirationFilter variants render dte as a list, a dashed range and an operator-prefixed comparison, each validated in its compact constructor and covered by wire and validation unit tests, with docs and the example updated. The weakness is the live suite: the boundary value for four of the new dte tests is taken from the unfiltered default chain response, and two of those tests (degenerate range, >= boundary) would pass unchanged if the server ignored dte entirely. Two points also need a person: the sealed interface's permits clause grew, and the live <N behaviour contradicts the API doc's word "inclusive".

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:350 — frontMonthRow() builds the boundary value from a chain call with no expiration filter, i.e. from what the server returns by default — the brief forbids that ("A baseline is built with the parameter under test, not from what the server returns by default"). All four boundary tests (chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds, chainDteComparisonGteIncludesTheBoundaryValue, chainDteComparisonGteExcludesExpirationsBelowTheBoundary, chainDteComparisonLtExcludesTheBoundaryValue) depend on it. Derive the boundary from a call that uses dte itself (e.g. the dte() of the row returned by chainRowForDte(ExpirationFilter.dte(k))).
  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:209 — chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds and chainDteComparisonGteIncludesTheBoundaryValue (line ~254) request a result identical to the unfiltered front-month answer: with frontDte as the bound, a server that ignored dte returns exactly the rows both tests assert (non-empty, every dte == frontDte / >= frontDte), so neither can fail on an ignored parameter. Use a boundary from a later expiration (so the expected rows differ from the default answer) and, for the >= case, also assert the answer spans more than one distinct expiration.
  • [minor] src/test/java/com/marketdata/sdk/OptionsResourceTest.java:1021 — The comparison form's wire test asserts only GTE (dte=%3E%3D30); the API's description names <45 explicitly and StrikeFilter.Operator has four constants, none of whose other renderings is asserted on the wire for dte. Add wire tests for at least LT (dte=%3C45) and ideally GT/LTE.
  • [minor · question for a person] src/main/java/com/marketdata/sdk/options/ExpirationFilter.java:25 — The change does touch an existing public member despite the probe's claim: the permits clause of the existing public sealed interface ExpirationFilter gains DteList, DteRange and DteComparison, so a consumer's exhaustive switch/Kotlin when over ExpirationFilter stops compiling. The repository's own review rules classify a new permitted subtype as additive, so this is noted for a person rather than as a break.
  • [minor · question for a person] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:301 — For a person to settle with the API team: GET /v1/options/chain/AAPL/?dte=<N&side=call&strikeLimit=1&range=itm (N = an existing expiration's dte) came back without the expiration at exactly N — chainDteComparisonLtExcludesTheBoundaryValue asserts that and passed live — while the API's description says "A closed range (0-45) or a comparison operator (>=30, <45) returns every expiration whose dte falls within the bounds, inclusive". "Inclusive" does not fit <, and "the syntax matches strike and delta" implies exclusive, so the test and the SDK text are consistent with the documentation's other statement; the author's summary did not name the disagreement.

4. review fix 1

Findings sent back to the coder:

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:350 — frontMonthRow() builds the boundary value from a chain call with no expiration filter, i.e. from what the server returns by default — the brief forbids that ("A baseline is built with the parameter under test, not from what the server returns by default"). All four boundary tests (chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds, chainDteComparisonGteIncludesTheBoundaryValue, chainDteComparisonGteExcludesExpirationsBelowTheBoundary, chainDteComparisonLtExcludesTheBoundaryValue) depend on it. Derive the boundary from a call that uses dte itself (e.g. the dte() of the row returned by chainRowForDte(ExpirationFilter.dte(k))).
  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:209 — chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds and chainDteComparisonGteIncludesTheBoundaryValue (line ~254) request a result identical to the unfiltered front-month answer: with frontDte as the bound, a server that ignored dte returns exactly the rows both tests assert (non-empty, every dte == frontDte / >= frontDte), so neither can fail on an ignored parameter. Use a boundary from a later expiration (so the expected rows differ from the default answer) and, for the >= case, also assert the answer spans more than one distinct expiration.

Minor findings sent along, as optional:

  • [minor] src/test/java/com/marketdata/sdk/OptionsResourceTest.java:1021 — The comparison form's wire test asserts only GTE (dte=%3E%3D30); the API's description names <45 explicitly and StrikeFilter.Operator has four constants, none of whose other renderings is asserted on the wire for dte. Add wire tests for at least LT (dte=%3C45) and ideally GT/LTE.

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:25 — The change does touch an existing public member despite the probe's claim: the permits clause of the existing public sealed interface ExpirationFilter gains DteList, DteRange and DteComparison, so a consumer's exhaustive switch/Kotlin when over ExpirationFilter stops compiling. The repository's own review rules classify a new permitted subtype as additive, so this is noted for a person rather than as a break.
  • [minor · question for a person] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:301 — For a person to settle with the API team: GET /v1/options/chain/AAPL/?dte=<N&side=call&strikeLimit=1&range=itm (N = an existing expiration's dte) came back without the expiration at exactly N — chainDteComparisonLtExcludesTheBoundaryValue asserts that and passed live — while the API's description says "A closed range (0-45) or a comparison operator (>=30, <45) returns every expiration whose dte falls within the bounds, inclusive". "Inclusive" does not fit <, and "the syntax matches strike and delta" implies exclusive, so the test and the SDK text are consistent with the documentation's other statement; the author's summary did not name the disagreement.

Coder (12 turns, US$ 0.73, 2 min 52 s):

Final gradle test --no-daemon --console=plain: BUILD SUCCESSFUL. Final gradle build --no-daemon --console=plain: BUILD SUCCESSFUL (spotlessJavaCheck, jacocoTestCoverageVerification, check, build all passed after running spotlessApply once to fix a javadoc line-wrap violation).

Fixed both major findings in OptionsIntegrationTest.java: removed frontMonthRow() (built its baseline from the unfiltered default, which the brief forbids) and replaced it with anchorDte(seedDays), which derives the boundary from a dte(seedDays) call itself. The two tests the reviewer flagged for sharing a boundary with the default answer (chainDteRangeDegenerateToSingleDayIncludesBothInclusiveBounds, chainDteComparisonGteIncludesTheBoundaryValue) now anchor on dte(180) — far enough out that an ignored filter, which would echo the near-term default, fails; the GTE-includes test also now asserts the answer spans more than one distinct expiration. The other two (GteExcludesExpirationsBelowTheBoundary, LtExcludesTheBoundaryValue) keep their existing ignore-detecting logic but now anchor on dte(7) instead of the unfiltered default.
Addressed the minor finding: added LT, GT and LTE wire tests for dteComparison in OptionsResourceTest.java (GTE's test was already present), so all four StrikeFilter.Operator constants now have their own wire assertion for dte.
The "question for a person" about ExpirationFilter's permits clause: left as-is per the instruction not to resolve or argue it — it stands as the reviewer recorded it.
The "question for a person" about < being exclusive against documentation's "inclusive" wording: the SDK's javadoc/docs already avoid claiming either inclusive or exclusive for the comparison form, stating only "matches every expiration satisfying operator days" and "syntax mirrors StrikeFilter" — consistent with the case-2 handling the reviewer described, so no doc change was needed; left the question for a person as given.
gradle test --no-daemon --console=plain and gradle build --no-daemon --console=plain both pass (including spotless and jacoco coverage verification) after one spotlessApply pass for formatting.

Gates:

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

5. review 2

Reviewer (5 turns, US$ 0.89, 1 min 35 s): 1 blocking finding(s)

Both blocking findings are resolved: frontMonthRow() is gone and every boundary now comes from anchorDte(seed), which derives it from a dte(...) call, and the two 180-day-seeded tests plus the new distinct-expiration assertion can now fail on an ignored filter. The three added wire tests (LT/GT/LTE) match OptionsResource:321 (operator().wireValue() + days) and close the earlier minor. One test the fix touched is still not falsifiable.

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:311 — chainDteComparisonLtExcludesTheBoundaryValue can pass with dte ignored: anchorDte(7) is a near-term expiration, and the unfiltered default answer (the front-month expiration, whose dte is <= the anchor) satisfies the sole allSatisfy(dte < anchorDte) assertion whenever the closest-to-7 expiration is not the nearest one; an empty answer also passes vacuously. Seed the anchor far out (as the two sibling tests do) and add an assertion the default cannot satisfy — that the answer spans more than one distinct expiration — keeping the per-row bound as it is.

6. security

The pipeline:

  • the security reviewer did not run: the code review did not approve the change

7. assess

Analyst (2 turns, US$ 0.21, 28 s) — public surface:

The change extends the sealed ExpirationFilter hierarchy with three new permitted record subtypes (DteList, DteRange, DteComparison) and three new static factory methods (dte(int...), dteRange, dteComparison) to cover dte's new string-typed wire forms (list, range, comparison) introduced in API 2.0.0. The existing dte(int) factory, record, and wire behavior are untouched, and overload resolution keeps existing dte(int) call sites bound to the unchanged overload, so every member touched is purely additive to the public surface.

  • added: ExpirationFilter.dte(int...) — New varargs overload for the comma-separated dte list form; Java overload resolution prefers the existing fixed-arity ExpirationFilter.dte(int) for single-argument calls (phase 1 beats phase 3 variable-arity), so existing calls are unaffected.
  • added: ExpirationFilter.dteRange(int, int) — New static factory for the closed-range dte wire form.
  • added: ExpirationFilter.dteComparison(StrikeFilter.Operator, int) — New static factory for the comparison-operator dte wire form, reusing StrikeFilter.Operator.
  • added: ExpirationFilter.DteList — New permitted subtype of sealed interface ExpirationFilter (record with days() accessor and validating constructor); the sealed parent's permits clause changed but is not itself listed as changed per the repository's sealed-type rule.
  • added: ExpirationFilter.DteRange — New permitted subtype of sealed interface ExpirationFilter (record with min()/max() accessors and validating constructor).
  • added: ExpirationFilter.DteComparison — New permitted subtype of sealed interface ExpirationFilter (record with operator()/days() accessors and validating constructor).

Computed by the pipeline: minor; changelog entry written

8. changelog

Coder (6 turns, US$ 0.08, 23 s):

I've already called StructuredOutput in my previous response above. The changelog entry has been successfully added to CHANGELOG.md under the [Unreleased] section with the ### Added subsection, documenting all three new ExpirationFilter methods and sealed subtypes for API release 2.0.0.

Gates:

  • build, attempt 1: passed (6 s)

Unresolved findings

  • [major] src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:311 — chainDteComparisonLtExcludesTheBoundaryValue can pass with dte ignored: anchorDte(7) is a near-term expiration, and the unfiltered default answer (the front-month expiration, whose dte is <= the anchor) satisfies the sole allSatisfy(dte < anchorDte) assertion whenever the closest-to-7 expiration is not the nearest one; an empty answer also passes vacuously. Seed the anchor far out (as the two sibling tests do) and add an assertion the default cannot satisfy — that the answer spans more than one distinct expiration — keeping the per-row bound as it is.
  • the security reviewer did not run: the code review did not approve the change

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:25 — The change does touch an existing public member despite the probe's claim: the permits clause of the existing public sealed interface ExpirationFilter gains DteList, DteRange and DteComparison, so a consumer's exhaustive switch/Kotlin when over ExpirationFilter stops compiling. The repository's own review rules classify a new permitted subtype as additive, so this is noted for a person rather than as a break.
  • src/integrationTest/java/com/marketdata/sdk/OptionsIntegrationTest.java:301 — For a person to settle with the API team: GET /v1/options/chain/AAPL/?dte=<N&side=call&strikeLimit=1&range=itm (N = an existing expiration's dte) came back without the expiration at exactly N — chainDteComparisonLtExcludesTheBoundaryValue asserts that and passed live — while the API's description says "A closed range (0-45) or a comparison operator (>=30, <45) returns every expiration whose dte falls within the bounds, inclusive". "Inclusive" does not fit <, and "the syntax matches strike and delta" implies exclusive, so the test and the SDK text are consistent with the documentation's other statement; the author's summary did not name the disagreement.

Totals

  • Bump: minor
  • Coder runs: 4
  • Review runs: 2
  • Gates run: 8 (1 failed)
  • Files changed by the agent: 7
  • Turns: 111
  • Tokens: 11,556,169
  • Estimated cost: US$ 6.63
  • Time: 26 min 21 s (agents 24 min 21 s, gates 1 min 45 s)

Agent runs

  • coder · claude-haiku-4-5-20251001, claude-sonnet-5 · 2,241,969 tokens (126,893 in, 2,067,186 cached, 47,890 out) · 26 turns · US$ 1.41 · 8 min 27 s
  • coder · claude-sonnet-5 · 6,201,798 tokens (54,596 in, 6,117,480 cached, 29,722 out) · 54 turns · US$ 1.74 · 6 min 26 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 307,287 tokens (93,019 in, 194,585 cached, 19,683 out) · 6 turns · US$ 1.57 · 4 min 10 s
  • coder · claude-sonnet-5 · 2,410,898 tokens (28,772 in, 2,368,459 cached, 13,667 out) · 12 turns · US$ 0.73 · 2 min 52 s
  • reviewer · claude-haiku-4-5-20251001, claude-opus-5 · 213,576 tokens (61,199 in, 145,523 cached, 6,854 out) · 5 turns · US$ 0.89 · 1 min 35 s
  • analyst · claude-haiku-4-5-20251001, claude-sonnet-5 · 44,812 tokens (42,198 in, 0 cached, 2,614 out) · 2 turns · US$ 0.21 · 28 s
  • changelog · claude-haiku-4-5-20251001 · 135,829 tokens (28,720 in, 104,861 cached, 2,248 out) · 6 turns · US$ 0.08 · 23 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 (39491b7).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #102      +/-   ##
============================================
+ Coverage     98.27%   98.29%   +0.02%     
- Complexity     1043     1052       +9     
============================================
  Files           123      123              
  Lines          2606     2641      +35     
  Branches        301      309       +8     
============================================
+ Hits           2561     2596      +35     
  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 97.95% <100.00%> (+2.95%) ⬆️

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...39491b7. Read the comment docs.

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

@MarketDataDev03

Copy link
Copy Markdown
Collaborator

Closing to re-run the SDK sync for API 2.0.0 with a clearer rule for live filter tests and a third review cycle.

@MarketDataDev03
MarketDataDev03 deleted the sdk-sync/2.0.0-ffb34b7 branch October 30, 2026 16:10
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 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