Implement velocity-testkit: in-memory reference backend + conformance TCK - #5
Merged
Conversation
… TCK Proves the frozen velocity-spi contract is implementable and provides the harness every backend (JDBI/Redis) must pass (ADR 0004, NFR-13/18, acceptance #3). - InMemoryVelocityBackend: thread-safe, EXACT, ATOMIC reference backend implementing VelocityBackend + Count/Sum/DistinctStore + Sliding/TumblingSupport + SeedSupport. Injectable Clock (backend is the clock authority); raw-event store computed per window at read; sliding = (now-D, now], tumbling = aligned bucket; apply returns one PerFeature per (intent x feature window) and its value already reflects the write (read-your-write); down/unsupported -> distinguishable Failure, never a silent 0. - Conformance TCK (tck/*Scenarios, in src/main so backends reuse it): Count/Sum/ Distinct/Sliding/Tumbling/Seed/Capability/Purge scenarios (27 methods) that advance the clock and assert real windowed values, aging-out / bucket-reset, seeded==recorded merge (acceptance #16), unsupported-window Failure, and declaration<->implementation agreement. - InMemoryVelocityBackendTck drives all 27 scenarios; 48 tests total; coverage 97.8% line / 87% branch (gate 80/70). - velocity-spi: comment-only javadoc clarification on apply* result cardinality (one PerFeature per intent x window); no signature/type change. Validated by the change-validator agent: VERDICT APPROVE ("this TCK would fail a broken backend"). Removed unused testkit deps (jackson/caffeine/slf4j) per the review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wolpert
enabled auto-merge
July 19, 2026 00:10
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
velocity-testkit— the in-memory referencevelocity-spibackend and the mandatory conformanceTCK. This proves the freshly-frozen SPI is actually implementable (the architect's core concern) and
gives every future backend (JDBI, Redis, …) the harness it must pass (ADR 0004, NFR-13/18,
acceptance #3).
What's in it
InMemoryVelocityBackend— thread-safe, EXACT, ATOMIC reference backend implementingVelocityBackend+Count/Sum/DistinctStore+Sliding/TumblingSupport+SeedSupport. InjectableClock(the backend is the clock authority per ADR 0003); a raw-event store computes each window atread time — sliding =
(now-D, now], tumbling = aligned bucket[start, end).applyreturns onePerFeatureper (intent × the feature's windows) and its value already reflects the write(read-your-write). A down/unsupported result is a distinguishable
Failure, never a silent 0.tck/*Scenarios, shipped insrc/mainso backends reuse it): 27 scenariomethods across Count/Sum/Distinct/Sliding/Tumbling/Seed/Capability/Purge that advance the clock and
assert real windowed values, aging-out / bucket-reset, seeded ≡ recorded merge (acceptance #16),
unsupported-window →
Failure(UNSUPPORTED_WINDOW), and declaration↔implementation agreement.InMemoryVelocityBackendTckdrives all 27 scenarios; 48 tests, coverage 97.8% line /87% branch (gate 80/70) — met by genuinely exercising the backend, not padding.
SPI change
Comment-only: a javadoc clarification on
CountStore/SumStore/DistinctStoreapply*that theresult is one
PerFeatureper (intent × window). No signature/type change — the frozen contractis preserved (
git diff velocity-spiis javadoc-only).Review
Implemented against a precise spec, then reviewed by the repo's
change-validatoragent (CLAUDE.mdworking model). VERDICT: APPROVE — it verified the TCK is genuinely adversarial ("would fail a
silent-zero backend, a windowing-ignorer, or a wrong-capability backend"), the in-memory backend is
correct (incl. a 16-thread atomicity test), and coverage is honestly met. Its one worth-fixing nit —
unused testkit deps (jackson/caffeine/slf4j) — is fixed in this PR.
Follow-up (non-blocking, noted by the reviewer)
TCK completeness isn't framework-enforced — a future backend author could forget to wire a scenario.
Worth a later abstract
@TestFactory/base or a documented checklist so downstream backends can'tunder-wire. Not required now (the in-memory adapter wires all 27).
./gradlew :velocity-spi:build :velocity-testkit:build→ BUILD SUCCESSFUL.🤖 Generated with Claude Code