Context
PR #1669 (#1461) corrected the published cache-first loader contract on the two authorized docs
sources: getCachedEntry() is a KV-only metadata read that never evaluates staleness or schedules a
refresh, and the callable procedure action owns the stale-while-revalidate policy. The corrected
shape is action-then-metadata:
const data = await ordersQueries.list(input, { preferFreshOnStale: true });
const entry = await ordersQueries.list.getCachedEntry(input);
return entry ?? { data, cachedAt: Date.now() };
Two things were left out of that PR deliberately, both raised as non-blocking advisories and both
ruled out of its scope by the coordinator. They are recorded here so they are not lost.
(a) Cross-page consistency — three surfaces still on the older shape
docs/site/tutorials/live-dashboard/04-definePage-QueryIsland.md:124,
docs/site/web-layer/layers.md:184, and docs/site/index.vto:71 (homepage withLayer loader) still
demonstrate a bare getCachedEntry() loader rather than the action-then-metadata composition.
These pages are not wrong. They attach no revalidation claim to getCachedEntry, and their
loaders are compatible with a separate page/layer background policy — the homepage example even
carries an explicit cold-cache fallback to queryOptions(input).queryFn(). This is cross-page
consistency debt, not a correctness defect, and it was correctly excluded from #1669 rather than
widened into a passed PR.
The value in aligning them is that a reader moving from chapter 3 to chapter 4 currently meets two
different loader shapes for the same job, with no stated reason for the difference.
Scope if actioned: update the three loaders to the action-then-metadata composition, or add one
sentence to each explaining why the bare metadata read is the right choice for that surface. Either
resolves the inconsistency; the second is cheaper and may be more honest if those surfaces genuinely
intend a pure read.
Non-goals: no SDK source change; no new public API; no change to #1461's corrected pages.
(b) Warm-stale persistence-failure return shape
The documented entry ?? { data, cachedAt: Date.now() } fallback covers the cold-cache case
correctly: no entry, so the freshly fetched data is returned with a current timestamp.
It does not describe the warm-stale-plus-failed-persistence case. Under PR #1665's non-fatal
cache-write contract, a refresh whose fetch succeeds but whose store.set() fails returns the fetched
data to the caller and leaves the entry uncached — so the cache still holds the older entry.
getCachedEntry() then returns that older entry rather than null, the ?? fallback does not fire,
and the loader returns the stale entry.data and entry.cachedAt even though data in the same
scope is fresh.
The behaviour is correct at every layer — it is the documented shape that does not say what happens.
This is an edge-case documentation caveat, explicitly non-blocking for the proven common-path
acceptance in #1461.
Scope if actioned: state the case in the SDK docs, and decide and document the intended contract —
prefer the fresher data when the entry is older than the just-completed refresh, or prefer the
persisted pair for internal consistency. Both are defensible; the point is that the docs currently
choose neither.
Non-goals: no change to #1665's non-fatal write contract; no new option or return type unless the
chosen contract demands one, which would need its own scope ruling.
Acceptance
Provenance
Non-blocking advisories 2 and 3 of the #1669 IMPL-EVAL, evaluated head
9aa54ae2d4f53c705b0309ed472abf7bbccebe41, artifact commit 313cc08d5, comment
.../pull/1669#issuecomment-5303850473. Filed as follow-up rather than absorbed, per the coordinator
ruling that #1669 must not be widened and its PASS must not be invalidated.
Context
PR #1669 (#1461) corrected the published cache-first loader contract on the two authorized docs
sources:
getCachedEntry()is a KV-only metadata read that never evaluates staleness or schedules arefresh, and the callable procedure action owns the stale-while-revalidate policy. The corrected
shape is action-then-metadata:
Two things were left out of that PR deliberately, both raised as non-blocking advisories and both
ruled out of its scope by the coordinator. They are recorded here so they are not lost.
(a) Cross-page consistency — three surfaces still on the older shape
docs/site/tutorials/live-dashboard/04-definePage-QueryIsland.md:124,docs/site/web-layer/layers.md:184, anddocs/site/index.vto:71(homepagewithLayerloader) stilldemonstrate a bare
getCachedEntry()loader rather than the action-then-metadata composition.These pages are not wrong. They attach no revalidation claim to
getCachedEntry, and theirloaders are compatible with a separate page/layer background policy — the homepage example even
carries an explicit cold-cache fallback to
queryOptions(input).queryFn(). This is cross-pageconsistency debt, not a correctness defect, and it was correctly excluded from #1669 rather than
widened into a passed PR.
The value in aligning them is that a reader moving from chapter 3 to chapter 4 currently meets two
different loader shapes for the same job, with no stated reason for the difference.
Scope if actioned: update the three loaders to the action-then-metadata composition, or add one
sentence to each explaining why the bare metadata read is the right choice for that surface. Either
resolves the inconsistency; the second is cheaper and may be more honest if those surfaces genuinely
intend a pure read.
Non-goals: no SDK source change; no new public API; no change to #1461's corrected pages.
(b) Warm-stale persistence-failure return shape
The documented
entry ?? { data, cachedAt: Date.now() }fallback covers the cold-cache casecorrectly: no entry, so the freshly fetched
datais returned with a current timestamp.It does not describe the warm-stale-plus-failed-persistence case. Under PR #1665's non-fatal
cache-write contract, a refresh whose fetch succeeds but whose
store.set()fails returns the fetcheddata to the caller and leaves the entry uncached — so the cache still holds the older entry.
getCachedEntry()then returns that older entry rather thannull, the??fallback does not fire,and the loader returns the stale
entry.dataandentry.cachedAteven thoughdatain the samescope is fresh.
The behaviour is correct at every layer — it is the documented shape that does not say what happens.
This is an edge-case documentation caveat, explicitly non-blocking for the proven common-path
acceptance in #1461.
Scope if actioned: state the case in the SDK docs, and decide and document the intended contract —
prefer the fresher
datawhen the entry is older than the just-completed refresh, or prefer thepersisted pair for internal consistency. Both are defensible; the point is that the docs currently
choose neither.
Non-goals: no change to #1665's non-fatal write contract; no new option or return type unless the
chosen contract demands one, which would need its own scope ruling.
Acceptance
metadata read is correct there.
shape.
separate scope ruling before implementation.
Provenance
Non-blocking advisories 2 and 3 of the #1669 IMPL-EVAL, evaluated head
9aa54ae2d4f53c705b0309ed472abf7bbccebe41, artifact commit313cc08d5, comment.../pull/1669#issuecomment-5303850473. Filed as follow-up rather than absorbed, per the coordinatorruling that #1669 must not be widened and its PASS must not be invalidated.