From 2d6458f7b3638f882ba5f20c207edd3c446fb016 Mon Sep 17 00:00:00 2001 From: klappy <118073+klappy@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:11:22 +0000 Subject: [PATCH 1/2] canon(cache-fetches-and-parses): same-URL freshness, the cost the plumbing tax missed A URL-keyed parse cache never notices a new commit at the same URL. Observed on oddkit_gate 2026-09-21 (old parse on 3/40 calls 65 min after a canon merge). Rule added: a kept parse cache expires no later than the fetch tier beneath it. Also marks, without resolving, that 0.23.0 removed the encode cache this document still describes as kept. Appended; nothing rewritten. Receipt: oddkit PR #215. --- canon/principles/cache-fetches-and-parses.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/canon/principles/cache-fetches-and-parses.md b/canon/principles/cache-fetches-and-parses.md index a484863..43e9104 100644 --- a/canon/principles/cache-fetches-and-parses.md +++ b/canon/principles/cache-fetches-and-parses.md @@ -38,7 +38,7 @@ This is a direct extension of vodka architecture: the server stays thin by cachi A **parse product** is a structured value derived from a canon document by work that would be wasteful to repeat on every request. Four attestation points across the current tool sweep: -**Encoding types parse caching (0.18.0, encode).** `cachedEncodingTypes` holds the parsed `EncodingTypeDef[]` from walking 10+ encoding-type canon docs, extracting frontmatter, regex-matching `## Quality Criteria` tables, and parsing each row. Building this list per request would repeat markdown parsing for every encode call. Cached by `knowledgeBaseUrl` — invalidates cleanly when canon source changes. +**Encoding types parse caching (0.18.0, encode).** `cachedEncodingTypes` holds the parsed `EncodingTypeDef[]` from walking 10+ encoding-type canon docs, extracting frontmatter, regex-matching `## Quality Criteria` tables, and parsing each row. Building this list per request would repeat markdown parsing for every encode call. Cached by `knowledgeBaseUrl` — invalidates cleanly when the *URL* changes. It does not notice a new commit at the same URL; see **Same-URL freshness** under the plumbing tax (amended 2026-09-21). *Status note, 2026-09-21: oddkit 0.23.0 later removed this encode cache entirely, citing this principle; this paragraph still describes it as kept. That disagreement between this document and the shipped code is open and is not resolved by this amendment.* **Base prerequisites parse caching (0.19.0, challenge).** `cachedBasePrerequisites` holds the parsed `BasePrerequisite[]` from reading `odd/challenge/base-prerequisites.md`, matching the `## Prerequisite Overlays` table, and extracting rows. Similar I/O + regex work that would be pure waste to repeat. @@ -76,6 +76,8 @@ Every module-level cache of a microsecond derivation pays four costs that rebuil **Drift risk when source data changes.** If the parse product is updated (e.g., during live canon-refresh work, or at test boundaries), the derived cache must also be invalidated. Every derivation chain must be walked to find every downstream cache. Miss one, ship with data from two different canon states simultaneously. +**Same-URL freshness (added 2026-09-21 — a fifth cost, found in production).** A URL key only notices a *different* URL. The default knowledge base URL never changes, and a branch URL stays the same while its content moves, so a parse cache keyed by URL alone keeps its first parse until the isolate is retired — `cleanup_storage` resets only the isolate that serves that call. The fetch tier already keeps this promise (commit id, index and file module caches all expire at `MODULE_CACHE_TTL_MS`, five minutes; stored content is addressed by commit). A parse cache sitting above it with no expiry silently cancels that freshness. Observed 2026-09-21 on `oddkit_gate`: after klappy.dev `cb37f62` changed `odd/gate/*`, production still answered from the old parse on 3 of 40 identical calls 65 minutes later, every stale answer reporting `governance_source: "knowledge_base"`. The rule that follows: **a parse cache that is kept must expire no later than the fetch tier beneath it** (or be keyed by the commit the fetch tier resolved). Worst-case staleness is then the sum of the two limits, and that number is stated where operators will read it. The same missing expiry also lets a one-off fetch failure pin an isolate to a `"minimal"` fallback indefinitely. Receipts: oddkit PR #215; kitchen ticket `2026-09-21-oddkit-gate-cache-follows-canon`. + Rebuild per request sidesteps all four. The server has fewer mutable globals, no invalidation logic to reason about, no cleanup wiring to remember, and no cross-cache drift surface. The cost is a few microseconds per request. The savings are in the reduction of plumbing-induced bug classes, not in wall-clock speed. --- From 1470e44e8e3048fe2cf652c65b4c6eb5f856da24 Mon Sep 17 00:00:00 2001 From: klappy <118073+klappy@users.noreply.github.com> Date: Mon, 21 Sep 2026 15:39:19 +0000 Subject: [PATCH 2/2] canon points up, not down: remove the adopter (kitchen) reference from the receipt line Captain, 2026-09-21: 'How did kitchen language get into canon?! Point up not down.' Canon names its own product receipt (oddkit PR); the adopter's work unit points up at canon, never the reverse. --- canon/principles/cache-fetches-and-parses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/canon/principles/cache-fetches-and-parses.md b/canon/principles/cache-fetches-and-parses.md index 43e9104..78677b7 100644 --- a/canon/principles/cache-fetches-and-parses.md +++ b/canon/principles/cache-fetches-and-parses.md @@ -76,7 +76,7 @@ Every module-level cache of a microsecond derivation pays four costs that rebuil **Drift risk when source data changes.** If the parse product is updated (e.g., during live canon-refresh work, or at test boundaries), the derived cache must also be invalidated. Every derivation chain must be walked to find every downstream cache. Miss one, ship with data from two different canon states simultaneously. -**Same-URL freshness (added 2026-09-21 — a fifth cost, found in production).** A URL key only notices a *different* URL. The default knowledge base URL never changes, and a branch URL stays the same while its content moves, so a parse cache keyed by URL alone keeps its first parse until the isolate is retired — `cleanup_storage` resets only the isolate that serves that call. The fetch tier already keeps this promise (commit id, index and file module caches all expire at `MODULE_CACHE_TTL_MS`, five minutes; stored content is addressed by commit). A parse cache sitting above it with no expiry silently cancels that freshness. Observed 2026-09-21 on `oddkit_gate`: after klappy.dev `cb37f62` changed `odd/gate/*`, production still answered from the old parse on 3 of 40 identical calls 65 minutes later, every stale answer reporting `governance_source: "knowledge_base"`. The rule that follows: **a parse cache that is kept must expire no later than the fetch tier beneath it** (or be keyed by the commit the fetch tier resolved). Worst-case staleness is then the sum of the two limits, and that number is stated where operators will read it. The same missing expiry also lets a one-off fetch failure pin an isolate to a `"minimal"` fallback indefinitely. Receipts: oddkit PR #215; kitchen ticket `2026-09-21-oddkit-gate-cache-follows-canon`. +**Same-URL freshness (added 2026-09-21 — a fifth cost, found in production).** A URL key only notices a *different* URL. The default knowledge base URL never changes, and a branch URL stays the same while its content moves, so a parse cache keyed by URL alone keeps its first parse until the isolate is retired — `cleanup_storage` resets only the isolate that serves that call. The fetch tier already keeps this promise (commit id, index and file module caches all expire at `MODULE_CACHE_TTL_MS`, five minutes; stored content is addressed by commit). A parse cache sitting above it with no expiry silently cancels that freshness. Observed 2026-09-21 on `oddkit_gate`: after klappy.dev `cb37f62` changed `odd/gate/*`, production still answered from the old parse on 3 of 40 identical calls 65 minutes later, every stale answer reporting `governance_source: "knowledge_base"`. The rule that follows: **a parse cache that is kept must expire no later than the fetch tier beneath it** (or be keyed by the commit the fetch tier resolved). Worst-case staleness is then the sum of the two limits, and that number is stated where operators will read it. The same missing expiry also lets a one-off fetch failure pin an isolate to a `"minimal"` fallback indefinitely. Receipt: oddkit PR #215. Rebuild per request sidesteps all four. The server has fewer mutable globals, no invalidation logic to reason about, no cleanup wiring to remember, and no cross-cache drift surface. The cost is a few microseconds per request. The savings are in the reduction of plumbing-induced bug classes, not in wall-clock speed.