Skip to content

feat(cache): add opt-in dependency snapshot store provider - #4461

Draft
kwakayama wants to merge 20 commits into
mainfrom
fix/shared-dependency-snapshot-store
Draft

feat(cache): add opt-in dependency snapshot store provider#4461
kwakayama wants to merge 20 commits into
mainfrom
fix/shared-dependency-snapshot-store

Conversation

@kwakayama

@kwakayama kwakayama commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

This is an opt-in shared-cache implementation of DependencySnapshotStore, building on #4451. A trusted host installs createCacheDependencySnapshotStoreHandle() from veryfront/platform on its runtime adapter before requests begin. The framework does not activate storage from ambient configuration.

The original staging failure was a cold replica returning 409 for a dependency snapshot referenced by an older rendered document. #4459, now merged, addresses that incident through shared metadata-history recovery. This PR therefore needs to stand on its separate value: a reusable snapshot-store provider for explicitly configured hosts.

This PR remains draft because the current API and Redis publication path does not satisfy the store's immutable-key contract. A deterministic two-publisher interleaving reproduces two successful acknowledgements for different bytes at the same key. Read-back verification cannot make an unconditional write atomic across replicas. The existing CAS regression covers a synthetic revision-capable backend. Neither the API nor Redis implementation at this head or current main supplies getWithRevision/compareExchange. The snapshot provider now uses the reserved revisioned key format when the optional atomic capability is available. It still needs to require an atomic publication contract, distinguish backend failures from misses, and validate the actual qualifying production backend. Resolving this requires atomic storage support and corresponding production-backend tests. The unresolved review thread tracks that work.

The current implementation includes public opt-in wiring, validation, expiry handling, publication verification, and protection of private cache capabilities against the specific replaced-global hooks covered by tests. These checks are not evidence that all production-backend contract requirements are satisfied.

Validation at 2d3a7ce94932f1999a1c81a73deff1c3da7e212a, using Deno 2.7.7:

  • deno task test:file src/cache/dependency-snapshot-store.test.ts tests/integration/cache/dependency-snapshot-publication-deadline.test.ts: 2 tests and 29 steps passed.
  • A revisioned publication now retries an identical winner from its current revision when retention is shorter than requested. Retry attempts are bounded, cancellation stops further storage calls, and different bytes remain rejected.
  • Publication also rejects expiration during an accepted CAS, before retry, and while reading an identical winner. All three deadline regressions failed before the fix and pass afterward. Both the 1ms and 60-second shortfall regressions failed before the retry fix and pass afterward. Existing tests continue to reject dropped renewals, insufficient retained deadlines, and conflicting bytes.
  • Explicit types, lint, formatting, semantic audit, generated references, and diff checks pass.
  • Main conflicts are resolved with its stricter Sharp cache-identity assertion; Sharp and cache/store tests passed after that reconciliation. Fresh CI and Codex review are required for this head.

The atomic-publication and inherited-then backend-exposure findings remain open. A deterministic probe confirms that a Promise resolution invokes an inherited then hook with the raw backend, even after the species fix. Completing private asynchronous storage requires an opaque, non-thenable capability transport across initialization and cache reuse. This is additional storage-contract work; the PR remains draft. Passing validation of the addressed findings does not establish production correctness or capability isolation.

…ed cache

Staging preview health has been red since Sep 7: a fresh project renders its
document against the pre-writeback dependency snapshot key, dependency
writeback then rewrites package.json, and every replica that never resolved
the old key answers pinned module requests with 409 Unknown dependency
snapshot. The hydration runtime's static import graph dies on the first 409,
so the page never defines __veryfrontRenderPage (veryfront-e2e
preview-rendering.health.spec.ts, run 34250153773).

The shared-snapshot machinery from #4451 solves exactly this, but nothing
provided a DependencySnapshotStore in production, so history stayed
process-local. Implement the store over the shared cache backends the module
response caches already use (API cache or Redis; node-local disk and memory
backends never qualify), and attach the process-wide handle at
createHandlerDependencyPinningSource so document renders publish their
snapshot and module requests on cold replicas recover it.

Publication re-reads and verifies the written bytes because the backends fail
open on set, and different bytes at a published key reject rather than
overwrite, per the store contract.

Claude-Session: https://claude.ai/code/session_01GNVuKWr64KLJRUrZvZJ3c4
Copilot AI lite review requested due to automatic review settings September 8, 2026 19:17

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: e318a307-c741-474e-9337-e9a40d38e710

📥 Commits

Reviewing files that changed from the base of the PR and between c44ea65 and 9261b8c.

📒 Files selected for processing (7)
  • docs/api-reference/veryfront/platform.md
  • src/cache/backends/factory.ts
  • src/cache/bounded-read.ts
  • src/cache/capabilities.ts
  • src/cache/dependency-snapshot-store.ts
  • src/platform/index.ts
  • tests/integration/server/dependency-snapshot-store-wiring.test.ts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Adds a cache-backed dependency snapshot store with bounded records, expiry checks, conflict handling, and revision-aware publication. Adds runtime adapter wiring for shared API or Redis backends. Tests cover recovery, isolation, failure handling, concurrency, and capability boundaries.

Changes

Dependency Snapshot Storage

Layer / File(s) Summary
Store contract and record validation
src/cache/dependency-snapshot-store.ts, src/cache/dependency-snapshot-store.test.ts
Adds bounded record encoding and decoding, abort handling, expiry validation, malformed-record rejection, and bounded backend reads.
Publication and recovery flow
src/cache/dependency-snapshot-store.ts, src/cache/dependency-snapshot-store.test.ts
Adds immutable publication, identical-byte renewal, conflicting-write rejection, revision-aware compare-exchange, verified writes, and concurrent publication tests.
Shared backend runtime wiring
src/cache/dependency-snapshot-store.ts, src/platform/index.ts, tests/integration/server/dependency-snapshot-store-wiring.test.ts, docs/api-reference/veryfront/platform.md
Adds API or Redis backend resolution, an opaque runtime handle, the public platform export, and API reference documentation.
Intrinsic isolation and capability wiring
src/cache/backends/factory.ts, src/cache/bounded-read.ts, src/cache/capabilities.ts, tests/integration/server/dependency-snapshot-store-wiring.test.ts
Captures native operations for backend access and capability inspection. Tests cover replaced globals, inherited capability injections, and backend or stored-byte isolation.

Priority: ➖ Normal

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

Merge Risk: 🔵 Low · up to 9261b

Shared dependency snapshots enable cold-replica recovery, but one record-size enforcement path lacks direct test coverage. This is a bounded validation gap with low current production risk.

Sequence Diagram(s)

sequenceDiagram
  participant RuntimeAdapter
  participant DependencySnapshotStoreHandle
  participant DependencySnapshotStore
  participant SharedCacheBackend
  RuntimeAdapter->>DependencySnapshotStoreHandle: provide configured store handle
  DependencySnapshotStoreHandle->>DependencySnapshotStore: resolve shared backend
  DependencySnapshotStore->>SharedCacheBackend: publish dependency snapshot
  SharedCacheBackend-->>DependencySnapshotStore: verified retention
  RuntimeAdapter->>DependencySnapshotStoreHandle: read snapshot on cold replica
  DependencySnapshotStoreHandle->>DependencySnapshotStore: read namespace and key
  DependencySnapshotStore->>SharedCacheBackend: bounded read
  SharedCacheBackend-->>DependencySnapshotStore: stored snapshot
  DependencySnapshotStore-->>RuntimeAdapter: dependency bytes
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 7 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an opt-in cache-backed dependency snapshot store provider.
Full details: Docstring Coverage

Explanation

Docstring coverage is 41.18% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 7 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/shared-dependency-snapshot-store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-08T21:43:18.650715Z d9e09bc Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

📦 Client bundle boundary

Entrypoint Modules Source size Server leaks
src/index.client.ts 289 2304 KiB ✅ 0

A server module in a client graph aborts hydration in the browser. New leaks fail CI; known leaks are tracked in scripts/lint/client-bundle-baseline.json to burn down.

Comment thread src/cache/dependency-snapshot-store.ts Outdated
Comment thread src/cache/dependency-snapshot-store.ts Outdated

Copy link
Copy Markdown
Contributor Author

Review: 72/100 — solid root-cause fix with a real bounded-read gap against the store's own contract

Summary: Correctly diagnoses and fixes the staging health-check regression (process-local snapshot history vs. replicated writeback), with a well-reasoned implementation and a genuinely good regression test — but the store bypasses this codebase's bounded-read mechanism, so it doesn't actually satisfy the "bound reads to 1 MiB" clause of the DependencySnapshotStore contract it implements.

Strengths

  • Root-cause is nailed precisely: the writeup and the cold-replica scenario in dependency-pinning-source.test.ts reproduce the exact 409 flap window described in the linked staging run, and the fix targets the right seam (createHandlerDependencyPinningSource).
  • Correctly restricts "shared" backends to API cache / Redis and filters out disk/memory fallbacks (sharedBackendAccessor), matching the contract's explicit exclusion of node-local storage.
  • The read-back-verify-after-set pattern correctly handles the "backend fails open on set" case, and (traced through concurrent-writer scenarios) also correctly surfaces a lost race as a publish failure rather than silently coexisting bytes.
  • Test coverage on the store itself is good for the paths it does cover: round-trip, idempotent republish, different-bytes rejection, dropped-write detection, malformed records, unavailable backend.

Concerns

  1. (Primary) Unbounded read before the size check. src/cache/dependency-snapshot-store.ts's read/publish call backend.get()/backend.set() directly instead of this codebase's existing bounded-read path (readCacheValueWithinLimit / getWithinLimit in src/cache/bounded-read.ts). The interface doc comment this module implements (src/platform/adapters/dependency-snapshot-store.ts) says implementations "must ... bound reads to 1 MiB," and bounded-read.ts exists specifically so an oversized value "rejects ... before an untrusted backend can materialize" it. Here, the full raw string is read and JSON-parsed (decodeRecord) with no size check at all; the only size guard (DEPENDENCY_SNAPSHOT_MAX_BYTES) lives downstream in decodeDependencySnapshot, applied to the inner value field, after the whole record has already been materialized in memory. A bloated/corrupted entry at a snapshot key gets pulled entirely into process memory on every cold-replica read.
    • Note RedisCacheBackend doesn't currently implement getWithinLimit, so this can't be a drop-in swap to readCacheValueWithinLimit for both qualifying backends today — either add a manual assertCacheValueWithinLimit-style check after get() for both backends, or extend Redis with getWithinLimit first.
  2. publish/read accept the signal?: AbortSignal parameter from DependencySnapshotStore but never pass it to backend.get/backend.set, so the registry's 5s deadline doesn't actually cancel the underlying backend call — it just abandons it and lets it keep running.
  3. No test exercises oversized-value handling at the store layer itself (all size-limit testing lives elsewhere for the inner snapshot codec, none for the store's own record envelope).

None of these block the core fix from working correctly for the staging incident described, so I'd call this conditionally approvable — worth addressing #1 before/soon after merge given it's an explicit, deliberately-documented invariant of the interface this PR implements.


Generated by Claude Code

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 79ec86dcae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/server/handlers/utils/dependency-pinning-source.ts Outdated
Comment thread src/server/handlers/utils/dependency-pinning-source.ts Outdated
Comment thread src/cache/dependency-snapshot-store.ts Outdated
Comment thread src/cache/dependency-snapshot-store.ts
Comment thread src/cache/dependency-snapshot-store.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The new snapshot store implementation and its tests have contract/robustness gaps (expiry semantics, bounded reads, env-sensitive assertions) that should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR addresses cross-replica hydration failures during dependency writeback by wiring a shared DependencySnapshotStore into the handler dependency pinning source, so replicas can resolve historical dependency snapshot keys via distributed cache storage rather than relying on process-local history.

Changes:

  • Implement a cache-backed DependencySnapshotStore and expose a process-wide shared handle when a distributed cache backend is available.
  • Wire the shared snapshot store handle into createHandlerDependencyPinningSource, covering both publish (document render) and read (module/data requests) paths.
  • Add focused unit/regression tests for store semantics and cold-replica recovery after dependency writeback.

Verification

  • Not run in this environment (no task runner available via tools).
  • Recommended:
    • deno task test:file src/cache/dependency-snapshot-store.test.ts
    • deno task test:file src/server/handlers/utils/dependency-pinning-source.test.ts
File summaries
File Description
src/server/handlers/utils/dependency-pinning-source.ts Wires the shared dependency snapshot store handle into handler-scoped dependency pinning sources.
src/server/handlers/utils/dependency-pinning-source.test.ts Adds a cold-replica regression test to ensure pre-writeback keys can be recovered via shared history.
src/cache/dependency-snapshot-store.ts Introduces a distributed-cache-backed implementation of DependencySnapshotStore plus a lazily memoized shared handle.
src/cache/dependency-snapshot-store.test.ts Adds unit coverage for store round-trip/idempotency/error behavior and shared-handle behavior.
Review details

Suppressed comments (3)

src/cache/dependency-snapshot-store.ts:98

  • The publication verification re-read should also be bounded to 1 MiB (per the DependencySnapshotStore contract) so a corrupted/oversized cache entry rejects safely instead of being fully materialized.
      const writtenRaw = await backend.get(cacheKey);

src/cache/dependency-snapshot-store.ts:107

  • Reads should use CacheBackend.getWithinLimit when available to enforce the 1 MiB maximum payload requirement for dependency snapshot history records.
      const raw = await backend.get(recordKey(namespace, key));

src/cache/dependency-snapshot-store.test.ts:108

  • This assertion is env-sensitive (isApiCacheAvailable / isRedisConfigured). Without clearing/restoring relevant env keys, this test can fail if another suite configured a shared cache backend and did not restore the environment.
    it("returns undefined when no shared cache backend is configured", () => {
      assertEquals(getSharedDependencySnapshotStoreHandle(), undefined);
    });
  • Files reviewed: 4/4 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/cache/dependency-snapshot-store.ts Outdated
Comment thread src/cache/dependency-snapshot-store.ts Outdated
Comment thread src/cache/dependency-snapshot-store.ts Outdated
Comment thread src/cache/dependency-snapshot-store.test.ts Outdated
Address the local codex review of #4461:

- Reject node-local fallback backends inside the accessor factory so a Redis
  outage at initialization keeps the failure-retry path armed instead of
  pinning memory or disk as shared history for the life of the process.
- Defer to an adapter that configures its own dependencySnapshotStore
  (captured absence included); the cache-backed default applies only when the
  adapter says nothing.
- Verify the retained deadline, not just the bytes, after publication, with a
  small slack so concurrent same-value publishers acknowledge either write
  order while a silently dropped renewal still rejects.
- Publish through getWithRevision/compareExchange when the backend exposes
  the revision capability, so conflicting concurrent publications acknowledge
  at most one winner; the read-back-verified path remains for backends
  without it.
- Bound every record read (getWithinLimit where available, post-hoc byte
  assertion elsewhere) and enforce the 1 MiB payload limit before decoding
  or publishing.

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

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

@codex review

Second local review round on #4461:

- Local projects keep process-local history: a CLI-authenticated dev process
  can satisfy the shared-backend predicates without a cache-authorized tenant
  context, and a failing publication would break local rendering.
- Size the record bound for JSON escaping: payloads are JSON text, so
  worst-case embedding doubles quotes and backslashes; twice the 1 MiB
  payload bound plus envelope admits every valid payload. Near-limit
  escape-dense round-trip pinned.
- Document why the non-revisioned publish path's residual write race never
  surfaces legitimate divergence: snapshot bytes are the canonical
  serialization of exactly the state hashed into the key, so concurrent
  publishers at one key carry identical bytes; the checks still catch
  corruption, and backends that gain the revision capability get atomic
  publication automatically.

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

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 855c8cb69b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/dependency-snapshot-store.ts Outdated
…tion

Address PR review feedback and the CI failures on #4461:

- Honor the optional AbortSignal: operations reject promptly at entry and
  between backend round-trips. Backend calls themselves are not cancelable,
  matching the registry's advisory-cancellation model.
- read() returns null for a record a backend retained past its deadline; the
  contract reserves null for missing or expired history.
- Move environment-dependent tests (pinning rollout flags, shared-backend
  predicates) to tests/integration/server/dependency-snapshot-store-wiring.test.ts
  with explicitly pinned env, keeping the unit files hermetic. This fixes the
  semantic-disposition lint and the env-sensitive coverage-shard assertions.
- Document why the 60s renewal-deadline slack is safe: shared history can
  expire at most a minute early, and the registry renews half a retention
  period before expiry.
- Format.

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

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@kwakayama

Copy link
Copy Markdown
Contributor Author

All review feedback is addressed as of eb367da. Disposition of each finding:

Fixed in code

  • Bounded reads (review comment, Codex P2, Copilot): every record read goes through getWithinLimit where the backend exposes it, with a post-hoc assertCacheValueWithinLimit for Redis, plus a decoded-payload limit; record bound sized at 2× payload + envelope because payloads are JSON text (worst-case escaping doubles quotes/backslashes). Near-limit escape-dense round-trip and oversized-record tests added.
  • AbortSignal ignored (review comment fix: secure fs build pipeline #2, Gitar): operations now reject promptly at entry and between backend round-trips. The backend calls themselves aren't cancelable — same advisory-cancellation model the registry documents for its own 5s deadline.
  • Renewal deadline verification (Codex): publication verifies bytes and deadline; a silently dropped renewal rejects.
  • Adapter-provided store precedence (Codex P1): the cache-backed default applies only when the adapter exposes no dependencySnapshotStore own property (captured absence respected).
  • Local projects (Codex P1): never get the automatic store; process-local history preserved for local rendering.
  • Redis-outage fallback pinning (Codex P1): the accessor factory rejects node-local fallbacks so the failure-retry path stays armed and a recovered backend is picked up without a restart.
  • Expired records (Copilot): read() returns null for records a backend retained past their deadline.
  • Env-sensitive tests (Copilot + CI): env-dependent tests moved to tests/integration/server/dependency-snapshot-store-wiring.test.ts with explicitly pinned env; unit files are hermetic (also fixes the semantic-disposition lint and the coverage-shard flake).

Resolved with rationale rather than code

  • TOCTOU on non-revisioned publish (Gitar, Copilot, Codex P2): publication uses getWithRevision/compareExchange atomically when a backend exposes the revision capability (tested with a concurrent conflicting interleaving). Neither ApiCacheBackend nor RedisCacheBackend implements it today, and adding it means a Lua script for Redis and a server-side API change for the API cache — out of scope here. The residual race cannot surface legitimate divergence: a snapshot value is the canonical serialization of exactly the state hashed into its key (encodeDependencySnapshot sorts and canonicalizes — "Canonical bytes make repeated publication idempotent across replicas"), so two publishers at one key carry identical bytes unless storage is corrupted, which the byte checks still catch. Documented at the write site; backends that gain the capability get atomic publication automatically.
  • Fail-open reads during outage → 409 instead of 503 (Codex P1): CacheBackend.get cannot distinguish a miss from an outage on the current backends (both catch and return null). During an outage a cold replica degrades to today's pre-PR behavior (conflict), never to wrong data, and publication is protected by read-back verification. Making the backends fail closed is a broader change than this fix; noted in the module docs.
  • 60s renewal-deadline slack (Codex P2): the slack admits either write order for same-value publishers stamping deadlines ms apart. Its cost is bounded by its size — shared history can expire at most a minute before an acknowledged deadline, and the registry renews half a retention period (hours) before expiry, so a sub-minute shortfall never outlives the next renewal. Rejecting instead would fail renders on a benign race.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: eb367dae2a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.79592% with 25 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/cache/dependency-snapshot-store.ts 85.54% 25 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kojiwakayama

Copy link
Copy Markdown
Contributor

Reviewed eb367da and resolved five addressed threads: local-project isolation, adapter-store precedence, expired records, environment isolation, and the Gitar request to document non-atomic publication. The three focused suites pass all 30 steps. Codex also reports a clean review for this exact head.

Seven threads remain open because their requested guarantees are still only partially implemented or explicitly deferred:

  • Cancellation checks cover entry and the pre-write boundary, but an abort during backend resolution or a read can still be followed by another round-trip or a successful return.
  • The 60-second retention tolerance still acknowledges a deadline beyond the stored deadline after a dropped short renewal (two threads).
  • Redis still materializes the entire value before the size assertion (two threads).
  • Current API/Redis backends still lack atomic conditional publication.
  • A backend outage can still be reported as missing history because the production reads fail open.

The author documented these tradeoffs. They remain inconsistent with the corresponding store-contract guarantees, so I have kept those threads open for an explicit contract change or implementation fix.

@gitar-bot

gitar-bot Bot commented Sep 8, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your trial's included automatic processing has been used for this period. Upgrade now, or comment "Gitar review" to run a review anytime.
Learn more

Code Review ✅ Approved 2 resolved / 2 findings

Implements the shared DependencySnapshotStore over distributed cache backends to fix replica conflicts when dependency writebacks change snapshot keys mid-request. Cold replicas now recover rendered snapshots instead of serving 409 errors, resolving the staging e2e flaking since Sep 7.

Store now honors the optional AbortSignal from the contract. Publication uses compare-and-set for atomic writes when backend revisions are available, with documented fallback for non-atomic backends.

✅ 2 resolved
Quality: Store ignores the optional AbortSignal from the contract

📄 src/cache/dependency-snapshot-store.ts:76-90
The DependencySnapshotStore interface declares an optional signal?: AbortSignal on both publish and read, but this cache-backed implementation neither accepts nor forwards it to backend.get/backend.set. Callers passing a signal expecting cancellation get no cancellation, and the multiple sequential backend round-trips in publish will run to completion after abort. Accept and honor the signal (at minimum check signal?.aborted between round-trips) to match the contract.

Edge Case: publish read-modify-write is not atomic (TOCTOU)

📄 src/cache/dependency-snapshot-store.ts:80-94
publish reads the existing record, compares bytes, then sets in separate steps with no compare-and-set on the cache backend. Two replicas publishing genuinely different bytes at the same key concurrently can both read null (or an old value), both write, and both pass the read-back verification against their own value, so the documented 'different bytes rejection' guarantee is best-effort rather than enforced under concurrency. This is unlikely given content-derived keys, but if snapshot serialization is ever non-deterministic for the same dependency set the conflict would be silently accepted. Consider documenting the best-effort nature, or using an atomic add/CAS primitive if the backends expose one.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@kwakayama
kwakayama enabled auto-merge September 8, 2026 20:00
@kwakayama
kwakayama added this pull request to the merge queue Sep 8, 2026
@kojiwakayama

Copy link
Copy Markdown
Contributor

The latest head is a050c8a5408ffdae38c5b5541d2aee7e5d40fa27, including the sharp 0.35.4 security patch. The snapshot factory and private-capability fixes remain verified; the concurrency finding remains open.

One correction to the storage assessment: Redis has an optional atomic revision capability, advertised only after its topology/protocol probe succeeds. The current provider neither requires that capability nor uses the reserved revisioned key format, so simply reaching its optional CAS branch does not establish production correctness. The API backend has no revision capability. This reinforces the draft status and the need to test the actual qualifying backend path.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a050c8a540

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/dependency-snapshot-store.ts
@kojiwakayama

Copy link
Copy Markdown
Contributor

The revision-capable path now consistently uses reserved revisioned cache keys, including verification and reads. The public factory and captured capability fixes are also verified. CI is green at 4a849878c6610aec945c49515f02b4fab29748d0.

The non-revisioned publication race remains open and this PR stays draft. The description records the remaining storage-contract work and separates this provider from the already merged staging fix.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4a849878c6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/dependency-snapshot-store.ts Outdated
@kojiwakayama

Copy link
Copy Markdown
Contributor

The latest review findings are fixed and their regression tests pass at cabeb13ce8e2f724d7d266411143e0e4923f4ec0. Addressed threads are resolved. The atomic storage finding remains open and the PR remains draft.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cabeb13ce8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/backends/factory.ts Outdated
@kojiwakayama

Copy link
Copy Markdown
Contributor

The private-backend Promise species finding is fixed at a1f53d5740327d1109ff1c7f506fe932ad8fd426 with a failing-before/passing-after regression and synchronous initialization retry coverage. The atomic-publication thread remains open, so this PR stays draft.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1f53d5740

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


if (state.backend !== undefined) return Promise.resolve(state.backend);
if (state.backend !== undefined) {
return accessorApply(accessorPromiseResolve, AccessorPromise, [state.backend]) as Promise<

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prevent inherited then hooks from receiving the backend

When project code installs a callable Object.prototype.then before a store operation, this native Promise.resolve(state.backend) performs thenable assimilation and invokes the inherited hook with the cached API or Redis backend as this; initialization's return b has the same behavior. This is separate from the fixed species path and exposes the opaque backend and its credential-bearing client, while the hook can also resolve null to make storage appear unavailable. Ensure promises carry only an opaque, non-thenable token rather than the raw backend.

AGENTS.md reference: AGENTS.md:L110-L112

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Confirmed at a1f53d5 with a deterministic cached-backend reproducer. After successful initialization, installing a callable Object.prototype.then exposes the exact synthetic API backend as the hook receiver on the next accessor call (1 exposure, expected 0), and the hook can replace the resolved result with null. Moving finally cleanup does not address native thenable assimilation. This requires an asynchronous capability contract that carries an opaque, non-thenable token instead of the raw backend, including the initialization path. I am leaving this finding open and keeping the PR draft alongside the atomic-publication blocker. The current Promise path is not ready to satisfy the claimed shared-realm private-storage contract.

@kojiwakayama

Copy link
Copy Markdown
Contributor

Exact retention validation is fixed at dda6f59c2c9f8370417e19655d8925321d98b894. Publication no longer acknowledges a retained deadline up to 60 seconds short. Regressions cover dropped renewals and a concurrent same-value winner one millisecond short; exact and longer retained deadlines remain accepted.

Validation: 5 tests, 53 steps, plus explicit types, lint, formatting, semantic audit, and generated references. This does not resolve the atomic-publication or inherited-then backend-exposure findings, so both threads remain open and the PR stays draft.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: dda6f59c2c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@kojiwakayama

Copy link
Copy Markdown
Contributor

@codex review

Please review head 606eab3. It resolves the new main conflict by retaining the stricter Sharp cache-identity assertion. Sharp and cache/store suites pass 4 tests and 44 steps; formatting, semantic audit, generated references, and diff checks pass. The two confirmed storage blockers remain open, and the PR remains draft.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 606eab3372

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/dependency-snapshot-store.ts Outdated
@kojiwakayama

Copy link
Copy Markdown
Contributor

@codex review

Please review e2edfe3, which fixes the identical-publication CAS retry finding. All 5 targeted suites (56 steps) and static/generated-reference checks pass. The two existing storage-contract blockers remain open and the PR remains draft.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2edfe3f69

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/dependency-snapshot-store.ts
@kojiwakayama

Copy link
Copy Markdown
Contributor

@codex review

Please review head 2d3a7ce. It fixes the publication-deadline finding with three failing-then-passing regressions. The targeted store/deadline suites pass 2 tests (29 steps), and types, lint, formatting, semantic audit, and generated-reference checks pass. The two existing storage-contract blockers remain open, so this PR remains draft.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d3a7ce949

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cache/dependency-snapshot-store.ts
@sonarqubecloud

sonarqubecloud Bot commented Sep 9, 2026

Copy link
Copy Markdown

@kojiwakayama

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@kojiwakayama

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 2d3a7ce949

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

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.

3 participants