mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-26 15:35:07 +00:00
fix(server): fold the alias map into CONFIG_EPOCH so alias repoints invalidate the cache
Cache keys hash the model string exactly as the client sent it — `const model = parsed.model || modelsConfig.aliases.sonnet` (server.mjs:2806), which then goes straight into `cacheHash(model, ...)`. A request for "opus" is therefore cached under the literal string "opus", not under the canonical id it resolved to. models.json is read once at boot, so repointing an alias only takes effect on restart — and the response cache is SQLite-backed (keys.mjs getCachedResponse queries the `response_cache` table), so it OUTLIVES that restart. CONFIG_EPOCH folded four config values into every cache key for exactly this hazard (#176 / #177) but omitted the alias map. Consequence: an operator running CLAUDE_CACHE_TTL > 0 who repoints an alias keeps being served the OLD model's answers under that alias until TTL expiry — silently defeating the repoint. Fix: add `modelsConfig.aliases` to the epoch digest, so any alias change is an instant whole-cache invalidation. This is the mechanism #177 already established for the other config inputs, applied to the one it missed; it covers the normal, structured and singleflight paths at once because they all key off the same epoch, and it needs no change at the three call sites. Reproduced end-to-end on the real server, both directions (NODE_ENV=test + OCP_DIR_OVERRIDE, isolated SQLite store, CLAUDE_CACHE_TTL=600000): 1. boot with aliases.opus = claude-opus-4-8, request model:"opus" -> claude_spawned model=claude-opus-4-8, response cached 2. stop, repoint aliases.opus -> claude-sonnet-5, restart 3. same request again: on origin/main : cache_hit (stale Opus 4.8 answer, no spawn) on this branch : cache MISS -> claude_spawned model=claude-sonnet-5 Default CLAUDE_CACHE_TTL is 0 (caching off), so shipped default behavior is unaffected; this bites only operators who deliberately enabled the cache. cli.js citation: NOT APPLICABLE — no cli.js-derived wire behavior is touched. Scope justified under ALIGNMENT.md Rule 2 as OCP-owned cache bookkeeping: cli.js has no response cache and no equivalent operation, and the spawn arguments and protocol are unchanged. Found by an independent codex review (gpt-5.6-sol, reasoning=high) of the Opus 5 alias repoint in #192; verified first-hand before applying rather than taken on the reviewer's word. Tests: 449 passed, 0 failed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017gbqUZ8HfBZpjjbzQ85oH8
This commit is contained in:
+8
-1
@@ -393,8 +393,15 @@ const NO_CONTEXT = process.env.CLAUDE_NO_CONTEXT === "true";
|
||||
// Deliberately boot-time-only: runtime-mutable settings (e.g. maxPromptChars via the settings
|
||||
// API) are excluded because a const epoch cannot track them; truncation also only drops
|
||||
// context rather than changing the instruction set.
|
||||
// The ALIAS MAP is folded in for the same reason. Cache keys hash the model string exactly as
|
||||
// the client sent it (`const model = parsed.model || aliases.sonnet`), so a request for "opus"
|
||||
// is cached under the literal "opus" — not under the canonical id it resolved to. models.json is
|
||||
// read once at boot, so repointing an alias (e.g. opus: 4-8 -> 5) only takes effect on restart,
|
||||
// and the SQLite response_cache outlives that restart. Without the alias map in the epoch, an
|
||||
// operator running CLAUDE_CACHE_TTL > 0 keeps being served the OLD model's answers for the alias
|
||||
// until TTL expiry — silently defeating the repoint. Same bug class as #176.
|
||||
const CONFIG_EPOCH = cryptoCreateHash("sha256")
|
||||
.update(JSON.stringify([SYSTEM_PROMPT, SYSTEM_PROMPT_WRAPPER, ALLOWED_TOOLS, NO_CONTEXT]))
|
||||
.update(JSON.stringify([SYSTEM_PROMPT, SYSTEM_PROMPT_WRAPPER, ALLOWED_TOOLS, NO_CONTEXT, modelsConfig.aliases]))
|
||||
.digest("hex").slice(0, 16);
|
||||
// Kill-switch for the FIX-③ default-path spawn-home isolation (see resolveSpawnHome /
|
||||
// spawnHomeMode below). When "1", the -p/stream-json spawn always runs in the operator's
|
||||
|
||||
Reference in New Issue
Block a user