Skip to content

Compliance wave 5 part 1: explorer, archiving and FetchLatest suites (2.39.4) - #633

Merged
jeremydmiller merged 2 commits into
mainfrom
compliance/wave-5
Aug 4, 2026
Merged

Compliance wave 5 part 1: explorer, archiving and FetchLatest suites (2.39.4)#633
jeremydmiller merged 2 commits into
mainfrom
compliance/wave-5

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Compliance wave 5, part 1 — three more suites from the portable-today group of the backlog (marten#5118). 19 tests, library 105 → 124.

Suite Tests Covers marten issue
EventStoreExplorerCompliance 6 GetRecentStreamsAsync, GetStreamMetadataAsync, TryCreateUsage marten#5146
StreamArchivingCompliance 6 ArchiveStream and its consequences marten#5140
FetchLatestCompliance 7 FetchLatest / ProjectLatest marten#5138

Verified against working copies of both stores: Marten 124/124, Polecat 124/124 plus its full suite at 1722/0/3. Zero capability gates on either store.

What adoption found — two real Polecat defects, both now fixed

This is the payoff argument for the shared suite, so it's worth being concrete: neither of these was caught by either product's own tests.

  • polecat#411EventStoreUsage carries the event registry twice, as Events and RegisteredEventTypes. Marten fills both; Polecat filled only the latter, so usage.Events came back empty. This descriptor is read out of repo by CritterWatch, where an empty Events list reads as "this store has no event types configured" rather than "this store describes them under a different key".
  • polecat#412GetStreamMetadataAsync returned Tags: null!, but StreamMetadata.Tags is declared as a non-nullable IReadOnlyDictionary<string, string>. "No tags" is spelled with an empty dictionary; null forces every consumer that trusts the declaration into a NullReferenceException. The null! had silenced the compiler's warning rather than answering it.

Both are fixed in polecat#413, so the assertions here stand unmodified and the two capability gates I added while diagnosing them have been removed again. An unused gate is an invitation to soften an assertion later, so the second commit takes them back out. SupportsExplorerSurface stays — the explorer methods are throwing default-interface methods, so a genuinely minimal store may not implement them at all.

A note on FetchLatest and pending events

Two drafts of fetch_latest_after_fetch_for_writing_and_save_is_not_stale asserted that FetchLatest sees uncommitted appends — first through a bare Append, then through the FetchForWriting handle. Both failed, and both times the claim was wrong rather than the store: neither product documents that behaviour, and Marten's own test for this path saves before reading back.

The suite now pins the documented sequence (fetch for writing → append → save → read back on the same session), which still guards the real risk in that path: a stale cached aggregate surviving the save. The dead end is recorded in the test's <remarks> so it isn't re-litigated.

Scope notes

StreamArchivingCompliance deliberately stops at behaviour. Physical partition movement — Marten's archived event partition and Polecat's equivalent — is storage layout and stays in each product's own tests. Where the two stores could legitimately differ (whether archived events are excluded from a default stream read or returned flagged), the suite asserts the invariant they must share: an archived stream's events must never come back claiming to be live.

JasperFxVersion 2.39.3 → 2.39.4, in this PR, per the --skip-duplicate publish trap.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde

jeremydmiller and others added 2 commits August 4, 2026 16:37
Three more shared suites, 19 tests, taking the library from 105 to 124.

- EventStoreExplorerCompliance (6) - GetRecentStreamsAsync, GetStreamMetadataAsync
  and TryCreateUsage. Worth pinning cross-store because these are throwing
  default-interface methods with an out-of-repo consumer (CritterWatch), so a
  half-built implementation fails nothing at compile time.
- StreamArchivingCompliance (6) - ArchiveStream and its consequences: the
  archived flag on stream state, idempotence, neighbour isolation, and that the
  recorded version survives archiving. Physical partition movement stays
  product-specific.
- FetchLatestCompliance (7) - FetchLatest/ProjectLatest, including agreement with
  a live fold and with a store where the aggregate is registered nowhere.

Three capability gates added to the fixture, all default true:
SupportsExplorerSurface, SupportsUsageEventTypeDescriptors and
SupportsStreamMetadataTags. The latter two exist because adoption found two real
Polecat defects (polecat#411, polecat#412) - an empty Events collection on the
usage descriptor, and a null Tags dictionary where StreamMetadata declares it
non-nullable. The assertions stay; the gates are temporary and tracked.

Marten 124/124, Polecat 123 passed + 1 gated skip.

Note on FetchLatest and pending events: two drafts asserted that FetchLatest sees
uncommitted appends - first through a bare Append, then through the FetchForWriting
handle - and both were wrong about the contract rather than finding a bug. Neither
product promises it, and Marten's own test for that path saves first. The suite now
pins the documented sequence (fetch for writing, append, save, read back on the same
session), which still catches the real risk there: a stale cached aggregate.

Refs JasperFx/marten#5138, #5140, #5146, #5118

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde
polecat#411 (empty usage.Events) and polecat#412 (null StreamMetadata.Tags) are
fixed, so neither store needs SupportsUsageEventTypeDescriptors or
SupportsStreamMetadataTags. Removing them rather than leaving unused capability
gates around: a gate nobody needs is an invitation to soften an assertion later.

SupportsExplorerSurface stays -- the explorer methods are throwing default
interface methods, so a minimal store may legitimately not implement them at all.

Polecat 124/124 with no gates.
jeremydmiller added a commit to JasperFx/polecat that referenced this pull request Aug 4, 2026
…412)

Both found by a new cross-store compliance suite (EventStoreExplorerCompliance,
JasperFx/jasperfx#633) rather than by Polecat's own tests -- which is the point
of the shared suite.

#411: EventStoreUsage carries the event registry twice, as Events
(List<EventDescriptor>) and RegisteredEventTypes (List<EventTypeDescriptor>).
Marten fills both; Polecat filled only RegisteredEventTypes, so usage.Events came
back empty. This descriptor is read out of repo by CritterWatch, where an empty
Events list reads as "this store has no event types configured" rather than "this
store describes them under a different key". Now populated from the same
AllKnownEventTypes() loop that already fed RegisteredEventTypes.

#412: GetStreamMetadataAsync returned Tags: null!, but StreamMetadata.Tags is
declared as a non-nullable IReadOnlyDictionary<string, string>. "No tags" is
spelled with an empty dictionary; null forces every consumer that trusts the
declaration into a NullReferenceException instead of an empty loop. The `null!`
silenced the compiler's warning rather than answering it. Now a shared static
empty dictionary, so there is no per-stream allocation.

Deliberately product-only: the compliance enrollment that proves these lands
separately, once the JasperFx release carrying the suite is out.

Closes #411
Closes #412

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde
@jeremydmiller
jeremydmiller merged commit 14ae6c8 into main Aug 4, 2026
1 check passed
@jeremydmiller
jeremydmiller deleted the compliance/wave-5 branch August 4, 2026 22:55
jeremydmiller added a commit to JasperFx/polecat that referenced this pull request Aug 4, 2026
…412) (#413)

Both found by a new cross-store compliance suite (EventStoreExplorerCompliance,
JasperFx/jasperfx#633) rather than by Polecat's own tests -- which is the point
of the shared suite.

#411: EventStoreUsage carries the event registry twice, as Events
(List<EventDescriptor>) and RegisteredEventTypes (List<EventTypeDescriptor>).
Marten fills both; Polecat filled only RegisteredEventTypes, so usage.Events came
back empty. This descriptor is read out of repo by CritterWatch, where an empty
Events list reads as "this store has no event types configured" rather than "this
store describes them under a different key". Now populated from the same
AllKnownEventTypes() loop that already fed RegisteredEventTypes.

#412: GetStreamMetadataAsync returned Tags: null!, but StreamMetadata.Tags is
declared as a non-nullable IReadOnlyDictionary<string, string>. "No tags" is
spelled with an empty dictionary; null forces every consumer that trusts the
declaration into a NullReferenceException instead of an empty loop. The `null!`
silenced the compiler's warning rather than answering it. Now a shared static
empty dictionary, so there is no per-stream allocation.

Deliberately product-only: the compliance enrollment that proves these lands
separately, once the JasperFx release carrying the suite is out.

Closes #411
Closes #412


Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
jeremydmiller added a commit to JasperFx/marten that referenced this pull request Aug 4, 2026
Enrolls the three shared suites that shipped in 2.39.4 (JasperFx/jasperfx#633):
fetch_latest_compliance (7), stream_archiving_compliance (6) and
event_store_explorer_compliance (6). Marten's compliance coverage goes from 105
to 124 tests with zero capability gates.

No Marten code changes -- every entry point the three suites exercise was already
on the shared JasperFx surfaces, so this is a version bump plus one file of empty
subclasses.

Verified on net9.0 against the published packages: 124/124 compliance.

Closes #5138
Closes #5140
Closes #5146


Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
jeremydmiller added a commit to JasperFx/polecat that referenced this pull request Aug 4, 2026
Enrolls the three shared suites that shipped in 2.39.4 (JasperFx/jasperfx#633):
fetch_latest_compliance (7), stream_archiving_compliance (6) and
event_store_explorer_compliance (6). Polecat's compliance coverage goes from 105
to 124 tests with zero capability gates.

No gates are needed because #413 already fixed what they would have covered: the
explorer suite found an empty usage.Events collection (#411) and a null
StreamMetadata.Tags (#412) while it was being written, and those landed
separately so they did not have to wait on a JasperFx release. The two temporary
gates were removed from the compliance library before it shipped, so these
assertions run unmodified here.

The version jump also picks up 2.39.2 and 2.39.3, which had not been adopted yet.

Verified on net9.0 against the published packages: 124/124 compliance.


Claude-Session: https://claude.ai/code/session_01VpDCvJcBDZerieJB4JEHde

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant