mirror of
https://github.com/teamchong/pxpipe.git
synced 2026-07-22 02:02:51 +02:00
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:
+17
-1
@@ -159,6 +159,20 @@ const DYNAMIC_BLOCK_TAGS = [
|
|||||||
'system-reminder',
|
'system-reminder',
|
||||||
] as const;
|
] 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): {
|
function splitStaticDynamic(text: string): {
|
||||||
staticText: string;
|
staticText: string;
|
||||||
dynamicText: string;
|
dynamicText: string;
|
||||||
@@ -190,12 +204,14 @@ function splitStaticDynamic(text: string): {
|
|||||||
// collapse. Surfacing the tag name as telemetry lets us detect that
|
// collapse. Surfacing the tag name as telemetry lets us detect that
|
||||||
// within hours of a Claude Code release.
|
// within hours of a Claude Code release.
|
||||||
const known = new Set<string>(DYNAMIC_BLOCK_TAGS);
|
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 sniffer = /<([a-zA-Z][a-zA-Z0-9_-]*)(?:\s[^>]*)?>[\s\S]*?<\/\1>/g;
|
||||||
const unknown = new Set<string>();
|
const unknown = new Set<string>();
|
||||||
let s: RegExpExecArray | null;
|
let s: RegExpExecArray | null;
|
||||||
while ((s = sniffer.exec(staticBuf)) !== null) {
|
while ((s = sniffer.exec(staticBuf)) !== null) {
|
||||||
const tag = s[1]!;
|
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 {
|
return {
|
||||||
|
|||||||
+1
-1
@@ -369,7 +369,7 @@ async function main(): Promise<void> {
|
|||||||
if (e.info?.unknownStaticTags && e.info.unknownStaticTags.length > 0) {
|
if (e.info?.unknownStaticTags && e.info.unknownStaticTags.length > 0) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[pixelpipe warn] unknown tag(s) in static slab: ${e.info.unknownStaticTags.join(', ')} ` +
|
`[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`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -322,6 +322,23 @@ describe('transform', () => {
|
|||||||
expect(info.unknownStaticTags).not.toContain('env');
|
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 () => {
|
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 sys = 'claude.md\n'.repeat(400) + '<env>\nWorking directory: /tmp\n</env>';
|
||||||
const body = new TextEncoder().encode(
|
const body = new TextEncoder().encode(
|
||||||
|
|||||||
Reference in New Issue
Block a user