mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-19 09:44:07 +00:00
The cache key hashed model + keyId + sampling params + raw messages, but the ANSWER also depends on boot-time server config that shapes the composed prompt and tool surface: CLAUDE_SYSTEM_PROMPT (newly load-bearing since #175), OCP_SYSTEM_PROMPT_WRAPPER, CLAUDE_ALLOWED_TOOLS, and CLAUDE_NO_CONTEXT. The cache store is SQLite-backed and survives restarts, so an operator who changed any of these and restarted kept serving answers composed under the OLD config until TTL expiry (found by the #175 independent reviewer). Fix: server.mjs computes CONFIG_EPOCH once at boot — a 16-hex sha256 digest of the four values — and passes it to cacheHash, which folds `ce:<epoch>|` into the key. Any config change = instant whole-cache invalidation (the honest behavior). Callers that omit configEpoch (tests, any older path) hash byte-identically to before — asserted by test. Runtime-mutable settings (maxPromptChars via the settings API) are deliberately excluded: a const epoch cannot track them, and truncation drops context rather than changing the instruction set (noted in the code comment). One-time side effect on upgrade: existing cache entries no longer match (keys now carry the epoch). Cache is off by default and TTL-bounded; a one-time miss storm is the cost of never serving stale-config answers again. ALIGNMENT.md Rule 2: no cli.js citation applies — cache-key composition is OCP-internal (Class B); no endpoint, header, or wire field changes. Tests: +2, mutation-proven (dropping the fold fails both). Suite 343/0. Closes #176 Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude <claude-opus-4-8> <noreply@anthropic.com>
This commit is contained in:
@@ -204,6 +204,23 @@ test("cacheHash includes temperature in hash", () => {
|
||||
assert.notEqual(h2, h3);
|
||||
});
|
||||
|
||||
// ── configEpoch (#176): a boot-config change must invalidate the persistent cache ──
|
||||
// Mutation-proof: drop the `ce:` fold in keys.mjs and the first test goes green-to-red.
|
||||
test("cacheHash: different configEpoch → different key (config change invalidates)", () => {
|
||||
const h1 = cacheHash("sonnet", msgs1, { configEpoch: "aaaa000011112222" });
|
||||
const h2 = cacheHash("sonnet", msgs1, { configEpoch: "bbbb000011112222" });
|
||||
assert.notEqual(h1, h2);
|
||||
});
|
||||
|
||||
test("cacheHash: same configEpoch is stable; absent epoch hashes byte-identically to pre-#176", () => {
|
||||
const e1 = cacheHash("sonnet", msgs1, { configEpoch: "aaaa000011112222" });
|
||||
const e2 = cacheHash("sonnet", msgs1, { configEpoch: "aaaa000011112222" });
|
||||
assert.equal(e1, e2);
|
||||
// absent-epoch calls (older callers, all pre-existing tests) must not change behavior
|
||||
assert.equal(cacheHash("sonnet", msgs1, {}), cacheHash("sonnet", msgs1));
|
||||
assert.notEqual(e1, cacheHash("sonnet", msgs1), "epoch-carrying key differs from legacy key");
|
||||
});
|
||||
|
||||
test("cacheHash includes max_tokens in hash", () => {
|
||||
const h1 = cacheHash("sonnet", msgs1, {});
|
||||
const h2 = cacheHash("sonnet", msgs1, { max_tokens: 100 });
|
||||
|
||||
Reference in New Issue
Block a user