carlos / platform Public

edge: correct the X-Carlos-Version freshness comment — cache hits can lag

Review caught the prior comment overclaiming "can't go stale": that's true
only for uncached responses. A cache hit answers from GetFor/writeCached
before the wrapped handler ever runs, replaying whatever header maybeStore
froze at store time, for up to DefaultCacheMaxTTL or until a Carlos-Purge —
Reload never purges e.cache. Comment-only fix; documents the true contract
(fresh uncached, TTL-bounded lag on a hit, fails safe since a stale entry
can only read as the OLD version, never false-positive as the new one).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 7bf188d115e4de09c01228307a14b9dc78329807 parent 30c99fa
1 file changed, +16 −3
  • internal/edge/edge.go +16 −3
diff --git a/internal/edge/edge.go b/internal/edge/edge.go
index 5f5f873a3..ad2df5014 100644
--- a/internal/edge/edge.go
+++ b/internal/edge/edge.go
@@ -298,9 +298,22 @@ func (e *Edge) Reload() error {
// The deploy watch's proof and the human "verify with the binary you
// built" tell (carlos-deploy spec §7): the version this route is
// serving, from the row the adopt pass stamps AFTER the restart.
- // Server:-header-grade disclosure, ruled public 2026-08-08. Reload
- // rebuilds these handlers on every registry change — Fingerprint is
- // content-accurate since #15 closed, so the header can't go stale.
+ // Server:-header-grade disclosure, ruled public 2026-08-08.
+ //
+ // Freshness contract: on every UNCACHED response, this is live —
+ // Reload rebuilds these handlers on every registry change, and
+ // Fingerprint is content-accurate since #15 closed, so an uncached
+ // header can't go stale. A CACHE HIT bypasses this handler entirely
+ // (ServeHTTP's GetFor/writeCached path, ahead of the wrapped proxy)
+ // and replays whatever header maybeStore froze at store time, so it
+ // can lag a promote by up to the cache's TTL (clamped to
+ // DefaultCacheMaxTTL, 5 minutes) or until a Carlos-Purge evicts the
+ // entry — Reload never purges e.cache. That lag is one-directional
+ // and fails safe for the watch: a stale entry only ever carries the
+ // OLD version, so it can read as "not yet" but never false-positive
+ // as the new one. A watcher that needs the fresh answer within the
+ // TTL window must bust the cache (e.g. a unique query string).
+
if t.Version != "" {
proxy = versionHeader(t.Version, proxy)
}