transform: KNOWN_STATIC_TAGS allowlist (silence <types> canary)

The canary in splitStaticDynamic flags tag-shaped blocks in the static
slab that aren't in DYNAMIC_BLOCK_TAGS — designed to catch a new
Claude Code release that ships a per-turn tag we'd accidentally bake
into the cached image. In production it has been firing on `<types>`
for 93% of real requests (125 of 134 in the user's events log), which
is noise: `<types>` is part of Claude Code's built-in tool
documentation, not a per-turn rotation.

Add a second list, KNOWN_STATIC_TAGS, that mirrors DYNAMIC_BLOCK_TAGS
in shape. The unknown-tag computation now subtracts BOTH lists, so a
tag is only "unknown" when it's in neither the per-turn set nor the
known-static set. Seed KNOWN_STATIC_TAGS with ['types'].

Updates the warn log in src/node.ts to mention both options so a
reviewer of a NEW unknown tag knows which list to extend.

Adds a regression test pinning that <types> in the static slab no
longer produces info.unknownStaticTags.
This commit is contained in:
teamchong
2026-05-18 17:41:51 -04:00
parent a5f61ba6ee
commit e219b37cd5
3 changed files with 35 additions and 2 deletions
+17 -1
View File
@@ -159,6 +159,20 @@ const DYNAMIC_BLOCK_TAGS = [
'system-reminder',
] as const;
/**
* Tag-shaped blocks that DO appear in the static slab and SHOULD be baked into
* the cached image. These are part of Claude Code's built-in system prompt /
* tool documentation, not per-turn injections, so they're stable across turns.
*
* The canary in splitStaticDynamic flags any tag-shaped block in the static
* slab that isn't in DYNAMIC_BLOCK_TAGS — designed to catch a new Claude Code
* release that ships a per-turn tag we'd accidentally cache. Without this
* allowlist, known-static tags like <types> trigger the canary on most turns
* and drown out the real signal. Add a tag here only after confirming it's
* static (appears in the cacheable part of the prompt, not rotated per turn).
*/
const KNOWN_STATIC_TAGS = ['types'] as const;
function splitStaticDynamic(text: string): {
staticText: string;
dynamicText: string;
@@ -190,12 +204,14 @@ function splitStaticDynamic(text: string): {
// collapse. Surfacing the tag name as telemetry lets us detect that
// within hours of a Claude Code release.
const known = new Set<string>(DYNAMIC_BLOCK_TAGS);
const knownStatic = new Set<string>(KNOWN_STATIC_TAGS);
const sniffer = /<([a-zA-Z][a-zA-Z0-9_-]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g;
const unknown = new Set<string>();
let s: RegExpExecArray | null;
while ((s = sniffer.exec(staticBuf)) !== null) {
const tag = s[1]!;
if (!known.has(tag) && tag.length <= 64) unknown.add(tag);
if (!known.has(tag) && !knownStatic.has(tag) && tag.length <= 64)
unknown.add(tag);
}
return {
+1 -1
View File
@@ -369,7 +369,7 @@ async function main(): Promise<void> {
if (e.info?.unknownStaticTags && e.info.unknownStaticTags.length > 0) {
console.warn(
`[pixelpipe warn] unknown tag(s) in static slab: ${e.info.unknownStaticTags.join(', ')} ` +
`— may need to add to DYNAMIC_BLOCK_TAGS in src/core/transform.ts`,
`— may need to add to DYNAMIC_BLOCK_TAGS (per-turn) or KNOWN_STATIC_TAGS (static) in src/core/transform.ts`,
);
}
+17
View File
@@ -322,6 +322,23 @@ describe('transform', () => {
expect(info.unknownStaticTags).not.toContain('env');
});
it('does not flag <types> as an unknown tag (it lives in KNOWN_STATIC_TAGS)', async () => {
const sys =
'claude.md\n'.repeat(400) +
'<types>\nstring\nnumber\n</types>\n' +
'<env>\nWorking directory: /tmp\n</env>';
const body = new TextEncoder().encode(
JSON.stringify({
model: 'claude',
messages: [{ role: 'user', content: 'hi' }],
system: sys,
}),
);
const { info } = await transformRequest(body);
// <types> is known-static; it should NOT show up as an unknown tag.
expect(info.unknownStaticTags).toBeUndefined();
});
it('omits unknownStaticTags when the static slab has no tag-shaped blocks', async () => {
const sys = 'claude.md\n'.repeat(400) + '<env>\nWorking directory: /tmp\n</env>';
const body = new TextEncoder().encode(