Skip to content

feat(chattemplate): grade each GGUF's chat template in a directory and compare the quants - #294

Open
Defilan wants to merge 2 commits into
checks/config-loader-injectionfrom
checks/quant-template-divergence
Open

Defilan wants to merge 2 commits into
checks/config-loader-injectionfrom
checks/quant-template-divergence

Conversation

@Defilan

@Defilan Defilan commented Oct 19, 2026

Copy link
Copy Markdown
Member

Stacked on #291 (check set tier1/0.12). This one is tier1/0.13.

A directory scan named its GGUF files unparsed for their metadata and never read their chat templates. So a repository of quants had no Chat template row, and a template hidden in one quant went unread. Each GGUF carries its own template, a serving stack reads the template of the file it loads, and a model page shows one file's. That is the gap Pillar's chat-template research points at (arXiv 2602.04653).

What it does

  • Every GGUF's templates are graded in a directory scan, each with that file's own special tokens.
    • Files that carry the same templates are graded once, as one part ("Q4_K_M.gguf and 23 more GGUF file(s) with the same chat templates").
    • Projectors, imatrix files, and later split shards carry no template and are skipped.
    • A directory with Hugging Face configs and GGUF files grades both in one row.
  • Versions are compared with each other. When the files don't all carry the same templates, each version is compared with the others as with a reviewed reference (chattemplate.Options.Siblings). This is the tier1/0.8 near-miss rule, with no reference input:
    • A version that renders like another on every standard probe but adds a content-conditional branch is a LEAD (quant-template-mismatch, high).
    • Any other difference is listed in the notes, not judged.

Why near-miss and not "any difference"

I measured first. I streamed only the metadata of every GGUF at pinned commits, from the top GGUF repositories of bartowski, unsloth, lmstudio-community, ggml-org, Qwen, TheBloke, MaziyarPanahi, mradermacher, Google, and Microsoft, plus 16 named multi-quant repos.

  • Corpus: 57 repositories read, 884 files. 47 carry templates in two or more files (712 files, 49 versions).
  • Two carry a second version, both benign:
    • bartowski/Mistral-Nemo-Instruct-2407-GGUF: the f32 file has an older template, from before tool calls were added.
    • unsloth/Qwen3.8-27B-GGUF: the multi-token-prediction draft has an older template.
  • Cost of a LEAD on any difference: it would withhold 2 of 47 benign repositories (4%).
  • With the near-miss rule: all 47 PASS, the two list their versions, and there are 0 quant-template-mismatch findings.
SOCAIR_QUANT_TEMPLATE_CORPUS=<dir> go test ./internal/engine -run RealQuantTemplates -v

The corpus is metadata stubs: GGUFs with no tensors that keep the templates, vocabulary, and special tokens. The method and the table are in docs/false-positive-baseline.md.

Tests and benchmark

  • ggufdir_test.go covers five cases:
    • a near-miss quant is a LEAD naming its siblings;
    • a stale quant is listed, not judged;
    • shared templates are graded once, and a GGUF-only directory now has a row;
    • a code-reaching template in one quant FAILs the directory;
    • Hugging Face and GGUF templates share a row.
  • Falsification:
    • with the sibling comparison disabled, the near-miss test fails;
    • with GGUF templates not read, all five fail.
  • Benchmark:
    • dir-gguf-quant-near-miss (LEAD with Socair's defaults; it PASSed under tier1/0.12);
    • control control-dir-gguf-quants, the Mistral-Nemo shape (clean).
    • Chat template now detects 11 of 12; overall 70 of 76; 0 of 19 controls flagged; 66 of 76 with defaults.

Limits (in the baseline doc)

  • The near-miss rule needs one clean sibling. If every quant carries the same backdoored template, its own analysis is what grades it.
  • A template that differs on a standard probe as well as under a trigger is listed, not judged.
  • Tier 2 trigger confirmation still reads only a directory's Hugging Face templates.

go test ./..., gofmt -l cmd internal, and go mod verify are clean.

… the quants

A directory scan named its GGUF files unparsed for their metadata and never
read their chat templates, so a repository of quants had no Chat template
row, and a template hidden in one quant was not read at all. Each GGUF
carries its own template, a serving stack reads the template of the file it
loads, and a model page shows one file's.

The row now grades each GGUF's templates with that file's special tokens.
Files that carry the same templates are graded once. When they differ, each
version is compared with the others as with a reviewed reference
(chattemplate.Options.Siblings): a version that renders like another on every
standard probe but adds a content-conditional branch is a LEAD
(quant-template-mismatch), the tier1/0.8 near-miss rule with no reference
input. Any other difference is listed in the notes, not judged.

Measured first: of 47 real GGUF repositories with templates in two or more
files (712 files, 49 versions), two carry a second version, both benign. One
is an f32 file and the other a multi-token-prediction draft, each with an
older template. A LEAD on any difference would have withheld both. With the
near-miss rule, all 47 PASS and the two list their versions. Check set
tier1/0.13.

Signed-off-by: Christopher Maher <chris@mahercode.io>
Signed-off-by: Christopher Maher <chris@mahercode.io>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant