docs(store): explain OwnedEventStream as the on-ramp to futures::Stream — PR4 of arc-subscription refactor#180
Merged
Conversation
…am — PR4 of arc-subscription refactor
PR4 was framed in the plan as "evaluate whether `OwnedEventStream`
and `IntoStream` simplify or delete now that subscription cursors
are Arc-based and 'static." The evaluation came back: no, they
don't. Cursor structs became 'static, but `EventStream::Item<'a>`
is still a GAT — `Item<'a>` is still a borrowing `PersistedEnvelope`
on the base cursors. The witness that `OwnedEventStream` carries
("this combinator's `Item<'a>` is, in fact, owned `T: 'static` —
here is the name of the type") cannot be expressed via a
`for<'a> Item<'a>: 'static` HRTB on stable Rust. The sub-trait
survives PR1–PR3 structurally unchanged.
What was missing was the design intent — *why* the bridge exists
at all, and *why* every owning-combinator impl matters even when
no in-tree caller exercises a particular chain. Without that
intent recorded, the natural reading invites YAGNI-style deletion
of impls whose chains are not yet exercised. This commit records
the intent so future readers (and reviewers) understand the
shape:
- Module-level doc gains a "Why the bridge exists" section
explaining the on-ramp pattern: implement small owning
combinators in the GAT world (`Map`, `TryMap`, `MapErr`), then
cross into `futures::Stream` via `.into_stream()` to ride the
full futures combinator ecosystem (`.take_while`, `.filter`,
`.chain`, `.then`, `.buffer_unordered`, `try_collect`, ...).
- Module-level doc gains a "Trait surface IS the contract"
section stating that each `OwnedEventStream` impl is one lane
on the on-ramp and that an impl whose chain has no in-tree
consumer is still load-bearing API — it defines what users
*can* express via `.into_stream()`.
- The "Implemented for [`Map`] and [`TryMap`]" bullet is updated
to mention `MapErr`, which has been an impl in this module
since PR #171 (the doc fell out of sync at that time).
- `IntoStream`'s `_marker: PhantomData<fn() -> M>` field gains
an inline doc-comment explaining why it is structurally needed
(`M` appears only in the trait bound `S: OwnedEventStream<M>`,
not in any field).
No source code changes. No trait shape changes. No impl additions
or removals. PR4 ships as docs-only.
See `docs/plans/2026-05-26-arc-subscription-deviations.md` under
"[PR 4 | Task 1] — Decision REVISED" for the full evaluation
history, including the controller's initial wrong call (Option B
with `MapErr` impl deletion on YAGNI grounds) and why it was
reversed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR4 of the 4-PR arc-subscription refactor. Docs-only. No source code or trait changes.
The plan framed PR4 as "evaluate whether `OwnedEventStream` and `IntoStream` simplify or delete now that subscription cursors are Arc-based and `'static`." The evaluation concluded they do not: cursor structs became `'static`, but `EventStream::Item<'a>` is still a GAT and base-cursor items are still borrowing `PersistedEnvelope<'a, M>`. The witness `OwnedEventStream` carries — "this combinator's `Item<'a>` is, in fact, owned `T: 'static`" — cannot be expressed as a `for<'a> Item<'a>: 'static` HRTB on stable Rust. The sub-trait survives PR1–PR3 structurally unchanged.
What was missing was a record of design intent. Without it, the natural reading of `owned.rs` invites YAGNI-style deletion of `OwnedEventStream` impls whose chains have no current in-tree caller (`.try_map(...).map_err(...).into_stream()`, for instance). This commit documents the intent so future readers — human or AI — understand why every owning-combinator impl matters.
Changes
`crates/nexus-store/src/stream/owned.rs` (51 lines added, 3 changed):
`docs/plans/2026-05-26-arc-subscription-deviations.md`:
Verification
Test plan
Context
This concludes the 4-PR arc-subscription refactor (PR #177 → #178 → #179 → this PR). The four PRs: