Skip to content

Commit 67c6fcb

Browse files
committed
docs: make roadmap issue refs auto-link and align log-level wording
1 parent d12e782 commit 67c6fcb

1 file changed

Lines changed: 50 additions & 45 deletions

File tree

docs/roadmap.md

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ yet exist.
1212
## Status legend
1313

1414
- **Planned** — confirmed worth doing; deferred only for scope/sequencing.
15-
- **Decision needed** — a real fork with material trade-offs; pick a direction before writing code.
15+
- **Decision needed** — a real fork with material trade-offs; pick a direction before writing
16+
code.
1617
- **Design** — forward-looking design for the (not-yet-started) code generator.
1718

1819
---
@@ -22,22 +23,22 @@ yet exist.
2223
The synchronous pipeline ships retry, bearer auth, and pagination; the asynchronous side does
2324
not yet have equivalents. These bring the async path up to parity.
2425

25-
### Async retry step at the RETRY stage — [#31] (Planned, large)
26+
### Async retry step at the RETRY stage — #31 (Planned, large)
2627
No `AsyncHttpStep` occupies `Stage.RETRY`, so async pipelines get no retry. Add a public
2728
`AsyncRetryStep : AsyncHttpStep` with `final override val stage = Stage.RETRY` mirroring the sync
2829
`RetryStep`, plus a concrete `DefaultAsyncRetryStep` that reuses `HttpRetryOptions` and a
2930
`ScheduledExecutorService` for non-blocking backoff (no thread parked during the delay). Reuse the
3031
existing classification/`Retry-After` logic. Should land after the retry-defaults reconciliation
3132
(#38) so both async and sync share one backoff source of truth.
3233

33-
### Async bearer-auth with background token refresh — [#32] (Planned, medium)
34+
### Async bearer-auth with background token refresh — #32 (Planned, medium)
3435
Add an async bearer-auth `AsyncHttpStep` and a non-blocking refresh path on the token provider
3536
(`fetchAsync(...) : CompletableFuture<BearerToken>`, defaulting to wrapping the blocking `fetch`),
3637
so a valid-but-near-expiry token is returned immediately while the refresh runs off-thread.
3738
Coordinate with the bearer-token eviction work (#33) so the sync and async steps share eviction
3839
and refresh semantics.
3940

40-
### Async pagination — [#34] (Planned, large)
41+
### Async pagination — #34 (Planned, large)
4142
Both pagination surfaces are blocking today. After the pagination unification decision (#30),
4243
add an async sibling that drives an iterative `executeAsync` re-arm, closing each page on
4344
completion, with `Flow`/`Flux` bridges living in the coroutines/reactor adapter modules (not in
@@ -49,38 +50,38 @@ completion, with `Flow`/`Flux` bridges living in the coroutines/reactor adapter
4950

5051
Confirmed features deferred for scope. None is a defect; each adds a genuinely missing capability.
5152

52-
### Shared instrumentation emitter — [#26] (Planned, medium)
53+
### Shared instrumentation emitter — #26 (Planned, medium)
5354
The sync and async instrumentation steps duplicate the emit/redact/preview/metrics logic, with a
5455
standing extraction marker. The two copies have already diverged once. Extract the stateless logic
5556
into an internal `InstrumentationEmitters` constructed from the values both steps need
5657
(`HttpInstrumentationOptions`, `ClientLogger`, `Clock`, the lazy metric instruments). Pure internal
5758
refactor — high value for preventing future drift between the two steps.
5859

59-
### Per-call options channel on the request context — [#27] (Planned, medium)
60+
### Per-call options channel on the request context — #27 (Planned, medium)
6061
`RequestContext` carries no per-call override channel. Add an immutable `RequestOptions`
6162
(per-phase timeout overlay, response-validation toggle, ad-hoc credential override) with
6263
`applyDefaults` merge semantics, keeping the transport SPIs single-method. Depends on the
6364
`Timeout` value type from #41.
6465

65-
### Per-phase `Timeout` value type — [#41] (Planned, medium)
66+
### Per-phase `Timeout` value type — #41 (Planned, medium)
6667
No core timeout type exists; per-phase timeouts are configured ad hoc per transport. Add an
6768
immutable `Timeout` (connect / read / write / request `Duration`s, read/write defaulting to
6869
request) that adapters translate to native settings, kept distinct from the retry total-timeout.
6970
Prerequisite for #27.
7071

71-
### AutoCloseable SSE stream — [#35] (Planned, medium)
72+
### AutoCloseable SSE stream — #35 (Planned, medium)
7273
The SSE surface is a bare `Sequence`, so a partially-consumed stream can leak the response. Add an
7374
`SseStream : AutoCloseable, Iterable<ServerSentEvent>` that owns the `Response` and closes it on
7475
stream close or partial consumption — mirroring the close-on-partial-consume invariant pagination
7576
already enforces.
7677

77-
### Multipart request body — [#61] (Planned, large)
78+
### Multipart request body — #61 (Planned, large)
7879
No multipart support today. Add a public `MultipartRequestBody : RequestBody` (immutable + Builder)
7980
with one shared frame-size function driving both `writeTo` and `contentLength`, file parts
8081
streaming zero-copy via `FileRequestBody` and non-file parts encoded through the `Serde` SPI (no
8182
Jackson in `sdk-core`).
8283

83-
### Opt-in resource leak detector — [#45] (Planned, medium)
84+
### Opt-in resource leak detector — #45 (Planned, medium)
8485
No leak detection exists. Add an internal, opt-in, log-only `LeakDetector` that WARNs when a
8586
caller-owned closeable becomes phantom-reachable unclosed, using a reflectively-obtained
8687
`java.lang.ref.Cleaner` so Java-8 bytecode no-ops on JDK 8 and the whole thing is gated behind a
@@ -92,38 +93,39 @@ system property. Never auto-closes.
9293

9394
These need a recorded decision (and sometimes a small spike) before any code is written.
9495

95-
### URL model: resolved `java.net.URL` vs deconstructed — [#29] (Decision needed)
96+
### URL model: resolved `java.net.URL` vs deconstructed — #29 (Decision needed)
9697
**Recommended:** keep a single opaque URL field on `Request` (do not explode into
9798
scheme/host/port/segments/query on the immutable model), but migrate the stored type from
9899
`java.net.URL` to `java.net.URI``URI` is parse-only, DNS-free, and already what the JDK
99100
transport needs. Preserve the existing textual, DNS-free equality. Record the decision in
100101
`docs/architecture.md`. Gates #28 and the typed-page generation in #56.
101102

102-
### First-class `QueryParams` multimap — [#28] (Planned, medium; gated on #29)
103+
### First-class `QueryParams` multimap — #28 (Planned, medium; gated on #29)
103104
`QueryParam` is a `TODO()` stub and `RequestRebuilder` does query-string surgery by splitting on
104105
`&` with single-value semantics. Add a public `QueryParams` multimap modeled on `Headers`
105106
(insertion-ordered, multi-value, explicit encoding rules). Lands after the URL-model decision so it
106107
projects into the chosen type.
107108

108-
### Unify the two pagination stacks — [#30] (Decision needed, large)
109+
### Unify the two pagination stacks — #30 (Decision needed, large)
109110
Two parallel pagination surfaces exist and are both public API: `http.paging`
110111
(`PagedIterable`/`PagedResponse`, BYO fetcher) and `pagination` (`Paginator`/`Page` + strategies,
111112
self-driving). **Recommended:** make the strategy-driven `Paginator` + `Page` the canonical
112113
surface (it owns the client, matches peer SDKs, and is the model the typed-page generation in #56
113114
builds on) and deprecate the other. A public-API redesign — decide before coding. Gates #34.
114115

115-
### Deep-array value-equality utility — [#49] (Decision needed, small)
116+
### Deep-array value-equality utility — #49 (Decision needed, small)
116117
A `contentEquals`/`contentHashCode` helper has no consumer in the current tree; it is tied to the
117118
future generated DTOs (with `ByteArray` fields). **Recommended:** defer until codegen needs it, to
118119
avoid adding public surface that immediately churns the API snapshot and the coverage floor with no
119120
caller. Fold into the codegen runtime when that lands.
120121

121-
### VirtualThreads close-event log level — [#10] (Decision needed, trivial)
122-
The `executor.closed` event is emitted at DEBUG (not INFO). **Recommended:** keep DEBUG (it matches
123-
every other async-adapter event and keeps clean-shutdown noise off INFO) and close the issue,
124-
optionally noting the intent in the `close()` KDoc. No code change beyond the optional doc line.
122+
### VirtualThreads close-event log level — #10 (Decision needed, trivial)
123+
The `executor.closed` event is emitted at the SDK's VERBOSE level (which maps to SLF4J DEBUG), not
124+
INFO. **Recommended:** keep VERBOSE (it matches every other async-adapter event and keeps
125+
clean-shutdown noise off INFO) and close the issue, optionally noting the intent in the `close()`
126+
KDoc. No code change beyond the optional doc line.
125127

126-
### Release automation — [#75] (Decision needed, gated on CI)
128+
### Release automation — #75 (Decision needed, gated on CI)
127129
Versioning, changelog, and publishing are manual, and the version string is duplicated across ~10
128130
build scripts. **Recommended (once CI lands):** record a decision on adopting release-please; if
129131
adopted, collapse the duplicated version to a single anchor and add a release workflow + Sonatype
@@ -139,67 +141,70 @@ clients over this toolkit. None is a defect, and most cannot become code until t
139141
its keystone runtime type (#50) exist. They are captured here (and several warrant short design-doc
140142
sections now) so the effort can start coherently.
141143

142-
### Keystone: dependency-free four-state JSON field model — [#50] (Design, large)
144+
### Keystone: dependency-free four-state JSON field model — #50 (Design, large)
143145
The foundation the rest of the model generation builds on: a dep-free sealed `JsonField<T>`
144146
(`Known` / `Missing` / `Null` / `Raw`) plus a `RawJson` tree in `sdk-core`, with all
145-
Jackson↔`RawJson` conversion confined to the `sdk-serde-jackson` adapter — mirroring how `Tristate`
146-
is split today. Almost every other codegen item depends on this. **Land a design doc first**
147-
(`docs/codegen-json-field.md`), then the runtime type, then the generator template.
147+
Jackson↔`RawJson` conversion confined to the `sdk-serde-jackson` adapter — mirroring how
148+
`Tristate` is split today. Almost every other codegen item depends on this. **Land a design doc
149+
first** (`docs/codegen-json-field.md`), then the runtime type, then the generator template.
148150

149151
### Generated model shape (all depend on #50)
150-
- **Thin models over a hand-written runtime — [#51]** (Design): generated models stay <100 lines
152+
- **Thin models over a hand-written runtime — #51** (Design): generated models stay <100 lines
151153
(fields + accessors) while the runtime owns the forward-compat machinery.
152-
- **`additionalProperties` pass-through — [#52]** (Design): capture unknown fields into an immutable
154+
- **`additionalProperties` pass-through — #52** (Design): capture unknown fields into an immutable
153155
`Map<String, RawJson>` so read-modify-write round-trips don't drop server-added fields.
154-
- **Unions as private-ctor + per-variant accessors + visitor — [#53]** (Design): Java-8-safe
156+
- **Unions as private-ctor + per-variant accessors + visitor — #53** (Design): Java-8-safe
155157
`oneOf` emission with a retained raw node and an `unknown(raw)` fallback.
156-
- **Forward-compatible enums — [#54]** (Design): open value + known/value pair so deserialization
158+
- **Forward-compatible enums — #54** (Design): open value + known/value pair so deserialization
157159
never throws on an unrecognized server value.
158-
- **Discriminator/const fields as defaulted raw values with dual accessors — [#65]** (Design):
160+
- **Discriminator/const fields as defaulted raw values with dual accessors — #65** (Design):
159161
fold into the #50 design effort.
160-
- **Optional `validate()`/`isValid()`/`validity()` triad — [#64]** (Design): opt-in, memoized,
162+
- **Optional `validate()`/`isValid()`/`validity()` triad — #64** (Design): opt-in, memoized,
161163
off the deserialize path; used only as last-resort union disambiguation behind a discriminator.
162164

163165
### Generated service/client shape
164-
- **Two-tier raw/cooked service methods — [#55]** (Design): a "cooked" method returning the parsed
166+
- **Two-tier raw/cooked service methods — #55** (Design): a "cooked" method returning the parsed
165167
body and a "raw" method returning a lazy `ParsedResponse<T>`. Builds on the `ResponseHandler`
166-
seam (#36, now in review) and the operation-params SPI (#57).
167-
- **Minimal `OperationParams` SPI — [#57]** (Design): projects an operation's inputs into
168+
seam (#36) and the operation-params SPI (#57).
169+
- **Minimal `OperationParams` SPI — #57** (Design): projects an operation's inputs into
168170
headers/query/path/body and feeds the context chain. Gated on the `QueryParams` multimap (#28).
169-
- **Curated operation overload set — [#58]** (Design): one canonical method per operation plus a
171+
- **Curated operation overload set — #58** (Design): one canonical method per operation plus a
170172
small curated overload set leaning on Kotlin default arguments, rather than the full overload
171173
cross-product.
172-
- **Lazy sub-service accessor tree — [#59]** (Design): `by lazy` sub-service accessors on a
174+
- **Lazy sub-service accessor tree — #59** (Design): `by lazy` sub-service accessors on a
173175
generated root client, reusing a nested raw-response impl.
174-
- **`withOptions(Consumer<Builder>)` returning a new immutable client — [#60]** (Design): gated on
176+
- **`withOptions(Consumer<Builder>)` returning a new immutable client — #60** (Design): gated on
175177
first deciding whether to introduce a single cloneable client-config with `toBuilder()`.
176-
- **Typed page classes that rebuild typed params — [#56]** (Design, xlarge): `nextPage()`
178+
- **Typed page classes that rebuild typed params — #56** (Design, xlarge): `nextPage()`
177179
re-invokes the operation with a typed param object, not a spliced URL string. Gated on #57, #28,
178180
and the pagination unification (#30).
179-
- **Per-endpoint SSE adapter — [#62]** (Design): maps an `SseStream` to a lazily-decoded
181+
- **Per-endpoint SSE adapter — #62** (Design): maps an `SseStream` to a lazily-decoded
180182
`Iterable<TModel>`. Gated on #35 and #36.
181-
- **Per-operation auth descriptors with a precedence ladder — [#63]** (Design): the generator emits
183+
- **Per-operation auth descriptors with a precedence ladder — #63** (Design): the generator emits
182184
an `AuthMetadata` per operation; `sdk-core`'s auth step consumes it scheme-agnostically. The
183185
scheme-agnostic primitives partly exist already.
184186

185187
### Generator plumbing & outputs
186-
- **Strict structured-output JSON-schema encoding rules — [#66]** (Design, small): capture the
188+
- **Strict structured-output JSON-schema encoding rules — #66** (Design, small): capture the
187189
encoding contract (all-required + `additionalProperties:false` + optional-as-nullable-union) as a
188190
design-doc section now; it is adapter-only, never `sdk-core`.
189-
- **Reusable fail-soft recursive validator skeleton — [#67]** (Design, small): a small generic
191+
- **Reusable fail-soft recursive validator skeleton — #67** (Design, small): a small generic
190192
recursion-guarded, path-prefixed validator idiom for the generator's own IR; build in codegen
191193
week 1–2, once the IR exists.
192-
- **Provenance file stamped into generated SDKs — [#68]** (Design, small): generator version +
194+
- **Provenance file stamped into generated SDKs — #68** (Design, small): generator version +
193195
input-contract hash, emitted into generated output only, never into the hand-written toolkit.
194-
- **Spring Boot starter per generated API — [#69]** (Design, medium): an optional sibling
196+
- **Spring Boot starter per generated API — #69** (Design, medium): an optional sibling
195197
`<api>-spring-boot-starter` with `@ConfigurationProperties`, a customizer `fun interface`, and an
196198
`@AutoConfiguration` assembling {IoProvider + transport + HttpPipeline}, keeping Spring out of
197199
`sdk-core` and the generated client.
198200

199201
### Suggested codegen sequencing
200202

201-
1. Design docs: #50 (keystone), #66, #67, #68 — these can be written now, before any generator code.
203+
1. Design docs: #50 (keystone), #66, #67, #68 — these can be written now, before any generator
204+
code.
202205
2. Land #50's runtime type in `sdk-core`; decide #29 (URL model) and #28 (`QueryParams`).
203-
3. Stand up the generator IR + the fail-soft validator (#67), then model emission (#51#54, #64, #65).
204-
4. Service/client emission (#55, #57, #58, #59, #60), then pagination/SSE/auth generation (#56, #62, #63).
206+
3. Stand up the generator IR + the fail-soft validator (#67), then model emission
207+
(#51#54, #64, #65).
208+
4. Service/client emission (#55, #57, #58, #59, #60), then pagination/SSE/auth generation
209+
(#56, #62, #63).
205210
5. Packaging outputs (#68 provenance, #69 Spring starter).

0 commit comments

Comments
 (0)