Skip to content

Add expiry-safe replay sets#65

Merged
flyingrobots merged 10 commits into
mainfrom
feat/expiring-set-lifecycle
Jul 13, 2026
Merged

Add expiry-safe replay sets#65
flyingrobots merged 10 commits into
mainfrom
feat/expiring-set-lifecycle

Conversation

@flyingrobots

Copy link
Copy Markdown
Member

Summary

  • add cas.expiringSets.open() with atomic addIfAbsent, targeted contains, expiry-only sweep, bounded inspect, and non-mutating doctor
  • persist digest-only marker pages in one RootSet-anchored structured bundle under refs/cas/expiring/*
  • fail closed on digest collisions, malformed or inconsistent state, expired retry windows, stale doctor generations, and clock rollback during sweep retries
  • document the storage shape, clock trust boundary, release semantics, and source-pinned lifecycle witness

Why

Security-sensitive replay markers must survive process restart and Git garbage collection for their entire acceptance window. Application code should not manage CAS objects, trees, refs, or an independent cache to get that behavior.

ExpiringSet deliberately has no live remove, repair, capacity, or LRU path. Release means publishing a parentless generation that no longer reaches expired support; this API never deletes objects or runs Git GC.

Linked Issues

Closes #53

Parent goalpost: #50

Proof

  • concurrent duplicate admission has exactly one winner
  • live markers survive arbitrary collection pressure and ordinary sweep
  • expired marker support becomes prunable only after explicit sweep
  • a fresh facade and plumbing adapter recover marker state from Git alone
  • real-Git tests verify the physical RootSet edge and transitive marker reachability
  • 55 source citations and the adversarial review record are in docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md

Validation

  • pnpm lint
  • pnpm test (209 files; 1,851 passed; 2 expected skips)
  • GIT_STUNTS_DOCKER=1 pnpm test:integration (9 files; 173 passed)
  • pnpm exec tsc --noEmit --skipLibCheck --module NodeNext --moduleResolution NodeNext --target ES2022 index.d.ts
  • git diff --check
  • Graft structural review: no breaking changes; additive minor semver impact

@flyingrobots

Copy link
Copy Markdown
Member Author

Self-review

Verdict: PASS. No unresolved findings.

I reviewed the complete main...feat/expiring-set-lifecycle range for API scope, replay-window integrity, publication order, concurrent writers, restart behavior, key privacy, collision handling, bounded residency, immutable lifecycle evidence, malformed-state behavior, clock semantics, and semver posture.

Key conclusions:

  • the only mutable authority is refs/cas/expiring/<namespace>; every generation is parentless and every marker is a real transitive tree edge
  • live markers have no remove, repair, capacity, or recency eviction path
  • duplicate rejection stages no object; conflict callbacks reset attempt-local state and re-evaluate expiry
  • reads are targeted and inventory/rewrite paths are streaming with bounded residency
  • malformed, missing, colliding, or generation-inconsistent state fails closed
  • release removes reachability only after expiry; no path deletes objects or invokes Git GC
  • Graft reports no removed export or breaking signature and classifies the API delta as additive minor

Validation is green locally: lint; 1,851 unit tests with 2 expected skips; 173 real-Git integration tests; TypeScript declaration compilation; citation validation; and git diff --check.

The full source-pinned review and residual constraints are recorded in docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md.

@flyingrobots

Copy link
Copy Markdown
Member Author

Code Lawyer Review

Verdict: PASS after nine adversarial findings were resolved.

  1. Digest domains could collapse under a faulty crypto adapter. Canonical, distinct domain outputs are now mandatory and primary collisions require the persisted verifier.
  2. Admission could outlive its requested window during a CAS retry. Every callback rechecks time, and the regression forces expiry between attempts.
  3. Doctor issue records could expose mutable nested data. Reports freeze the issue array and copied issue records.
  4. A logical bundle member path was incorrectly treated as a physical Git witness path. Evidence now names root-00000000; real Git separately proves transitive marker reachability.
  5. Canonical but inconsistent state could be silently healed. Mutation now replays historical marker edges before staging any write and fails closed on mismatch.
  6. Duplicate rejection could manufacture avoidable CAS residue. Existing live markers are checked before replacement staging; object counts remain unchanged.
  7. General cache policy could weaken replay protection. ExpiringSet rejects eviction policy and exposes no live removal or repair operation.
  8. Sweep could reuse stale time across a conflict retry. Each callback now captures the injected clock; rollback extends protection.
  9. Doctor could splice RootSet evidence from generation A with index state from generation B. A head mismatch now reports EXPIRING_SET_CONFLICT.

Source and regression evidence for every finding is pinned under the Code Lawyer Review section of the lifecycle witness. No open Code Lawyer findings remain.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added expiry-safe replay sets for atomically retaining replay markers within acceptance windows.
    • Added contains() membership checks, addIfAbsent() conditional admission, sweep() expiry-only release, plus inspect() and doctor() diagnostics.
    • Persist markers across restarts without storing plaintext keys; exposed the capability via the application storage API (with TypeScript types).
  • Documentation

    • Documented the expiring-set lifecycle, namespace/ref rules, privacy model, conflict handling, and operational semantics.
  • Tests

    • Added unit and integration coverage for persistence, expiry sweeps, concurrency, integrity/collision handling, inspection, and recovery.

Walkthrough

Adds a Git-backed ExpiringSet API for digest-only replay markers with atomic admission, restart persistence, expiry-only sweeping, inspection, doctor diagnostics, public facade/type wiring, documentation, and unit/integration coverage.

Changes

Expiring Set Lifecycle

Layer / File(s) Summary
Expiring-set contracts and value objects
src/domain/helpers/*, src/domain/value-objects/*, src/domain/services/ExpiringSetMetadataCodec.js, index.d.ts
Defines canonical key/ref validation, immutable marker data, strict metadata encoding, result/state interfaces, error codes, and public ExpiringSet types.
Structured-bundle index and state persistence
src/domain/services/ExpiringSetIndex.js, test/unit/domain/services/ExpiringSet.test.js
Stores state and marker pages, scans live/expired entries, and rewrites ordered references while excluding expired markers.
Admission, membership, sweeping, and diagnostics
src/domain/services/ExpiringSet.js, test/unit/domain/services/ExpiringSet.test.js
Implements digest-based contains, atomic addIfAbsent, expiry-only sweep, bounded inspect, immutable doctor reports, witness construction, conflict handling, and integrity validation.
Registry and store capability wiring
src/domain/services/ExpiringSetRegistry.js, index.js, test/unit/facade/*, test/unit/types/*
Adds namespace-scoped opening through cas.expiringSets.open(), lazy registry initialization, package exports, frozen capabilities, and declaration checks.
API documentation and lifecycle evidence
docs/API.md, docs/design/.../witness/expiring-set.md, README.md, STATUS.md, CHANGELOG.md, test/integration/expiring-set.test.js
Documents storage, privacy, expiry, clock, inspection, and doctor semantics, and validates restart survival, reachability, sweeping, concurrency, and pressure behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Poem

I’m a rabbit with markers tucked safely away,
No live one is evicted before its day.
Digests keep secrets, Git holds the thread,
Sweeps wait patiently till expiry is dead.
One winner hops through when racers appear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding expiry-safe replay sets.
Description check ✅ Passed The description covers the feature, linked issue, proof, and validation, though it is missing the template’s explicit release-impact section.
Linked Issues check ✅ Passed The PR implements the requested expiring set API, atomic concurrency behavior, injected clock, expiry-only sweep, and doctor evidence for all required states.
Out of Scope Changes check ✅ Passed The changes stay focused on expiring sets and supporting docs, types, tests, and helpers without obvious unrelated scope creep.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/domain/helpers/isCanonicalCollectionKey.js`:
- Around line 21-35: Confirm the project’s engines or minimum Node target
supports String.prototype.isWellFormed() (Node 20+), then replace the manual
isWellFormed loop with the native String.prototype.isWellFormed() call while
preserving the existing boolean validation behavior.

In `@src/domain/services/ExpiringSet.js`:
- Around line 308-338: Collapse the duplicate witness construction in
`#markerWitness` and `#indexWitness` into a single shared helper, preserving the
existing RetentionWitness fields and root metadata. Update both call sites to
use the shared helper while retaining their current argument semantics, so the
marker and index paths cannot diverge.

In `@src/domain/services/ExpiringSetMetadataCodec.js`:
- Around line 55-81: Update normalizeState to validate that state.updatedAt is
not earlier than state.createdAt, alongside the existing timestamp and
nextExpiry checks. Reject inconsistent states using the established invalid
validation path, while preserving all existing count and expiry invariants.

In `@src/domain/value-objects/ExpiringMarker.js`:
- Around line 30-38: In the ExpiringMarker construction flow, guard evidence for
null or undefined before the RetentionWitness normalization branch. Throw
invalid(...) using the existing EXPIRING_SET_MARKER_INVALID path, and only
instantiate RetentionWitness for non-nullish evidence while preserving existing
RetentionWitness instances.

In `@test/unit/domain/services/ExpiringSetMetadataCodec.test.js`:
- Around line 35-64: Add a direct assertion in the “ExpiringSet metadata
rejection” test that codec.encodeMarker rejects a marker whose
verificationDigest equals marker.keyDigest, expecting
EXPIRING_SET_MARKER_INVALID. Keep the existing non-canonical marker and
state-count assertions unchanged.

In `@test/unit/domain/value-objects/CacheCollectionValues.test.js`:
- Around line 42-44: Add a lone-surrogate rejection assertion alongside the
existing ExpiringSetKey combining-character test, using the same
EXPIRING_SET_KEY_INVALID error expectation. Keep the current valid-key assertion
and Unicode normalization coverage unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 962cf915-8ff4-41a3-8c90-5803b5898cd9

📥 Commits

Reviewing files that changed from the base of the PR and between dbf7424 and 1be13b3.

📒 Files selected for processing (23)
  • CHANGELOG.md
  • README.md
  • STATUS.md
  • docs/API.md
  • docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md
  • index.d.ts
  • index.js
  • src/domain/errors/Codes.js
  • src/domain/helpers/isCanonicalCollectionKey.js
  • src/domain/services/ExpiringSet.js
  • src/domain/services/ExpiringSetIndex.js
  • src/domain/services/ExpiringSetMetadataCodec.js
  • src/domain/services/ExpiringSetRegistry.js
  • src/domain/value-objects/CacheKey.js
  • src/domain/value-objects/ExpiringMarker.js
  • src/domain/value-objects/ExpiringSetKey.js
  • src/domain/value-objects/ExpiringSetRef.js
  • test/integration/expiring-set.test.js
  • test/unit/domain/services/ExpiringSet.test.js
  • test/unit/domain/services/ExpiringSetMetadataCodec.test.js
  • test/unit/domain/value-objects/CacheCollectionValues.test.js
  • test/unit/facade/ContentAddressableStore.application-storage.test.js
  • test/unit/types/declaration-accuracy.test.js
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: test-docker (node)
  • GitHub Check: test-docker (bun)
  • GitHub Check: test-docker (deno)
🧰 Additional context used
📓 Path-based instructions (4)
CHANGELOG.md

📄 CodeRabbit inference engine (AGENTS.md)

Use CHANGELOG.md to record the historical truth of merged behavior

Files:

  • CHANGELOG.md
STATUS.md

📄 CodeRabbit inference engine (AGENTS.md)

Use STATUS.md as a compact snapshot of release and runtime truth

Files:

  • STATUS.md
README.md

📄 CodeRabbit inference engine (AGENTS.md)

Use README.md as the public front door, core value prop, and quick start documentation

Files:

  • README.md
docs/design/**

📄 CodeRabbit inference engine (AGENTS.md)

Use docs/design/ directory for durable design contracts and proof plans

Files:

  • docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md
🧠 Learnings (2)
📚 Learning: 2026-02-28T19:21:13.982Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 15
File: test/unit/domain/services/CasService.envelope.test.js:326-330
Timestamp: 2026-02-28T19:21:13.982Z
Learning: In fuzz tests for cryptographic operations, use a seeded PRNG (e.g., xorshift32) for control-flow variables such as plaintext size selection and recipient index selection to ensure reproducibility. Continue to use randomBytes() for cryptographic keys and nonces to preserve realistic randomness and avoid security anti-patterns. This guideline applies to test files that perform fuzz testing of crypto logic; implement a consistent seed setup (e.g., fixed seed in test initialization) and document the rationale to enable deterministic replays across runs.

Applied to files:

  • test/unit/types/declaration-accuracy.test.js
  • test/unit/domain/value-objects/CacheCollectionValues.test.js
  • test/unit/domain/services/ExpiringSetMetadataCodec.test.js
  • test/unit/facade/ContentAddressableStore.application-storage.test.js
  • test/integration/expiring-set.test.js
  • test/unit/domain/services/ExpiringSet.test.js
📚 Learning: 2026-03-30T19:53:48.000Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 29
File: docs/design/TR-010-planning-index-consistency-review.md:121-131
Timestamp: 2026-03-30T19:53:48.000Z
Learning: In this repo, CHANGELOG.md entries should be user-facing and descriptive (bullet points) rather than cycle-ID headings (e.g., use a phrase like “Planning-index consistency review” instead of a “TR-010 — …” heading). When searching the changelog, don’t rely on grepping for cycle IDs (e.g., “TR-010”) because it may cause false negatives; search for the descriptive keywords/phrases from the bullet entries instead.

Applied to files:

  • CHANGELOG.md
🪛 ast-grep (0.44.1)
test/integration/expiring-set.test.js

[warning] 4-4: Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawnSync } from 'node:child_process';
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process)

🔇 Additional comments (39)
CHANGELOG.md (1)

50-55: LGTM!

README.md (1)

30-33: LGTM!

Also applies to: 106-106, 128-130

STATUS.md (1)

71-77: LGTM!

docs/API.md (6)

1562-1601: LGTM!


1602-1622: LGTM!


1623-1639: LGTM!


1640-1655: LGTM!


1675-1683: LGTM!


1663-1667: 🎯 Functional Correctness

inspect() cap matches the implementation
inspect() is capped at 1,000 records per call.

docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md (4)

1-23: LGTM!


24-101: LGTM!


222-224: 📐 Maintainability & Code Quality | ⚡ Quick win

Citation end line is off by one.

docs/API.md#1562-1682 should be docs/API.md#1562-1683 — the added "Expiring Sets" section runs through line 1683 (the Clock Trust closing sentence), which this citation currently omits.

📝 Proposed fix
-[cite: `docs/API.md#1562-1682@bf66f6e5247284975631ffdfaabdc15fdb0e25d4`]
+[cite: `docs/API.md#1562-1683@bf66f6e5247284975631ffdfaabdc15fdb0e25d4`]
As per coding guidelines, `docs/design/**` is used "for durable design contracts and proof plans," so citation accuracy in this witness matters for its evidentiary purpose.

Source: Coding guidelines


226-234: LGTM!

test/integration/expiring-set.test.js (4)

1-48: LGTM!


50-91: LGTM!


93-107: LGTM!


110-131: LGTM!

src/domain/errors/Codes.js (1)

25-28: LGTM!

src/domain/helpers/isCanonicalCollectionKey.js (1)

1-12: LGTM!

src/domain/value-objects/CacheKey.js (1)

1-3: LGTM!

Also applies to: 25-27

src/domain/value-objects/ExpiringSetKey.js (1)

1-28: LGTM!

src/domain/value-objects/ExpiringMarker.js (2)

12-29: LGTM!


65-78: LGTM!

src/domain/value-objects/ExpiringSetRef.js (1)

12-19: 🎯 Functional Correctness

No ref-specific error code is expected here. ExpiringSetRef follows the same pattern as CacheSetRef, and both use COLLECTION_NAMESPACE_INVALID for prefix mismatches; RootSetRef is the separate outlier with ROOT_SET_REF_INVALID.

			> Likely an incorrect or invalid review comment.
src/domain/services/ExpiringSetMetadataCodec.js (3)

1-45: LGTM!


84-95: LGTM!


97-160: LGTM!

index.d.ts (1)

360-386: LGTM!

Also applies to: 851-914, 1144-1150, 1199-1199

test/unit/domain/value-objects/CacheCollectionValues.test.js (1)

6-7: LGTM!

Also applies to: 17-17

test/unit/types/declaration-accuracy.test.js (1)

96-99: LGTM!

src/domain/services/ExpiringSetIndex.js (3)

26-117: LGTM!


159-254: LGTM!


133-157: 🎯 Functional Correctness

No change needed: iterateMemberReferences() yields canonical path order, and markerPath() uses fixed-length hex digests, so this merge logic stays in ascending digest order.

test/unit/domain/services/ExpiringSet.test.js (1)

28-480: LGTM!

src/domain/services/ExpiringSet.js (2)

34-306: LGTM!


340-520: LGTM!

src/domain/services/ExpiringSetRegistry.js (1)

14-101: LGTM!

index.js (1)

17-18: LGTM!

Also applies to: 69-69, 96-96, 186-188, 232-233, 291-314, 373-378

test/unit/facade/ContentAddressableStore.application-storage.test.js (1)

8-9: LGTM!

Also applies to: 41-48, 64-65, 78-87

Comment thread src/domain/helpers/isCanonicalCollectionKey.js
Comment thread src/domain/services/ExpiringSet.js Outdated
Comment thread src/domain/services/ExpiringSetMetadataCodec.js
Comment thread src/domain/value-objects/ExpiringMarker.js
Comment thread test/unit/domain/services/ExpiringSetMetadataCodec.test.js
Comment thread test/unit/domain/value-objects/CacheCollectionValues.test.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/domain/services/ExpiringSet.js (1)

394-409: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Validate every injected dependency during construction.

The check only validates rootSet.mutate, index.scan, and crypto.sha256, while later methods call clock.now, rootSet.read/doctor, and index methods such as fromRootEntries, getMarker, stageMarker, rewrite, markers, getState, and toRootEntry. A partially wired adapter therefore passes construction and fails later with a raw TypeError instead of INVALID_OPTIONS. Validate all required methods, including a custom clock, and add regression coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/domain/services/ExpiringSet.js` around lines 394 - 409, Expand
ExpiringSet.#assertDependencies to validate every method used by the service,
including clock.now, rootSet.read/doctor, and index.fromRootEntries, getMarker,
stageMarker, rewrite, markers, getState, and toRootEntry, while retaining the
existing checks. Ensure any missing method is reported through the existing
INVALID_OPTIONS error path during construction, and add regression coverage for
partially wired dependencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/domain/services/ExpiringSet.js`:
- Around line 394-409: Expand ExpiringSet.#assertDependencies to validate every
method used by the service, including clock.now, rootSet.read/doctor, and
index.fromRootEntries, getMarker, stageMarker, rewrite, markers, getState, and
toRootEntry, while retaining the existing checks. Ensure any missing method is
reported through the existing INVALID_OPTIONS error path during construction,
and add regression coverage for partially wired dependencies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dde5bb83-f5cc-485a-b304-39993e8f4edb

📥 Commits

Reviewing files that changed from the base of the PR and between 1be13b3 and 3809d0a.

📒 Files selected for processing (5)
  • docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md
  • src/domain/services/ExpiringSet.js
  • src/domain/value-objects/ExpiringMarker.js
  • test/unit/domain/value-objects/ApplicationStorageResults.test.js
  • test/unit/domain/value-objects/CacheCollectionValues.test.js
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
docs/design/**

📄 CodeRabbit inference engine (AGENTS.md)

Use docs/design/ directory for durable design contracts and proof plans

Files:

  • docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md
🧠 Learnings (2)
📚 Learning: 2026-02-28T19:21:13.982Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 15
File: test/unit/domain/services/CasService.envelope.test.js:326-330
Timestamp: 2026-02-28T19:21:13.982Z
Learning: In fuzz tests for cryptographic operations, use a seeded PRNG (e.g., xorshift32) for control-flow variables such as plaintext size selection and recipient index selection to ensure reproducibility. Continue to use randomBytes() for cryptographic keys and nonces to preserve realistic randomness and avoid security anti-patterns. This guideline applies to test files that perform fuzz testing of crypto logic; implement a consistent seed setup (e.g., fixed seed in test initialization) and document the rationale to enable deterministic replays across runs.

Applied to files:

  • test/unit/domain/value-objects/CacheCollectionValues.test.js
  • test/unit/domain/value-objects/ApplicationStorageResults.test.js
📚 Learning: 2026-07-13T17:00:46.222Z
Learnt from: flyingrobots
Repo: git-stunts/git-cas PR: 65
File: src/domain/helpers/isCanonicalCollectionKey.js:21-35
Timestamp: 2026-07-13T17:00:46.222Z
Learning: In git-stunts/git-cas, the codebase is intended to run on multiple JavaScript runtimes (Node, Bun, and Deno), not just Node. During code review, don’t suggest replacing existing portable helper logic with newer Node-only built-ins (e.g., Node 20+ APIs like `String.prototype.isWellFormed()`) unless the change preserves Bun/Deno compatibility (via portable/polyfill implementations or safe runtime gating). If a file includes manual implementations (such as `src/domain/helpers/isCanonicalCollectionKey.js`), treat them as intentional to avoid requiring a higher host built-in baseline.

Applied to files:

  • src/domain/value-objects/ExpiringMarker.js
  • src/domain/services/ExpiringSet.js
🔇 Additional comments (7)
src/domain/value-objects/ExpiringMarker.js (1)

11-62: LGTM!

Also applies to: 64-66, 68-81

test/unit/domain/value-objects/CacheCollectionValues.test.js (1)

6-7: LGTM!

Also applies to: 17-18, 42-46

test/unit/domain/value-objects/ApplicationStorageResults.test.js (1)

5-5: LGTM!

Also applies to: 184-195

src/domain/services/ExpiringSet.js (1)

1-32: LGTM!

Also applies to: 34-392, 413-496

docs/design/0047-application-storage-cache-boundary/witness/expiring-set.md (3)

15-15: LGTM!


205-232: LGTM!


253-253: LGTM!

@flyingrobots flyingrobots merged commit 1f10e5c into main Jul 13, 2026
6 checks passed
@flyingrobots flyingrobots deleted the feat/expiring-set-lifecycle branch July 13, 2026 17:08
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.

Add an expiry-safe replay set

1 participant