Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
f302241
Plan the M7 measurement contract and gate site generation on PRs
devYRPauli Aug 5, 2026
8f6a2fc
Distinguish a combination we could not measure from one that failed
devYRPauli Aug 5, 2026
9e9f763
Merge 1A: distinguish not-measurable from failed
devYRPauli Aug 5, 2026
eacff51
Add a tool that redacts local paths and re-anchors the evidence chain
devYRPauli Aug 5, 2026
bad9c09
Merge 3A: local-path redaction tool
devYRPauli Aug 5, 2026
b9464e5
Redact the producing machine's paths from six runs and re-anchor thei…
devYRPauli Aug 5, 2026
b96db7f
Redact the remaining path from the case-study copy and the spec
devYRPauli Aug 5, 2026
eafe2a3
Stop the generator from rendering an absolute path it is handed
devYRPauli Aug 5, 2026
21297e6
Add schema v3 wire types and a normalized measurement
devYRPauli Aug 5, 2026
910cae9
Give the scenario corpus a content-sensitive identity
devYRPauli Aug 5, 2026
7364a30
Merge 4A: schema v3 wire types
devYRPauli Aug 5, 2026
8f37448
Merge 4B: canonical corpus identity
devYRPauli Aug 5, 2026
9163cc1
Freeze the corpus that produced every published result
devYRPauli Aug 5, 2026
f5a477a
Recover immutable provenance for all 32 published rows
devYRPauli Aug 5, 2026
91d3556
Add the mark system and design tokens the analysis views will need
devYRPauli Aug 5, 2026
877b853
Merge m7-4c
devYRPauli Aug 5, 2026
d3bb292
Merge m7-5r
devYRPauli Aug 5, 2026
3c850db
Merge m7-design
devYRPauli Aug 5, 2026
e421016
Resolve model identity from an explicit registry, never from a name
devYRPauli Aug 6, 2026
3e57c6f
Merge 4D: registry resolver and endpoint sanitizer
devYRPauli Aug 6, 2026
1ea457f
Build the v3 contract where the run happens
devYRPauli Aug 6, 2026
283fc8c
Merge 4E: runtime contract builder and structured failures
devYRPauli Aug 6, 2026
bd15cbc
Map every published result to an explicit registry entry
devYRPauli Aug 6, 2026
27b5d45
Merge 5A: registry and 32-entry migration manifest
devYRPauli Aug 6, 2026
ed0f9f1
Recover model identity from inside the measured bytes
devYRPauli Aug 6, 2026
d0ffc63
Merge 5A2: GGUF in-artifact identity recovery
devYRPauli Aug 6, 2026
b1443fb
Emit v3 from new runs and keep legacy files in their own schema
devYRPauli Aug 6, 2026
cf7073a
Merge 4F: CLI emits v3, legacy edits keep their version
devYRPauli Aug 6, 2026
a38e063
Add a migration command driven by an explicit manifest
devYRPauli Aug 6, 2026
b415f66
Merge 5B: v3 migration command
devYRPauli Aug 6, 2026
e8922e6
Migrate published results to v3
devYRPauli Aug 6, 2026
a4a47e4
Merge 5C: 32-file v3 migration
devYRPauli Aug 6, 2026
21a6f3f
Label rows by what they are, not by their file name
devYRPauli Aug 6, 2026
a40ed27
Merge 5D: site uses registry identity
devYRPauli Aug 6, 2026
17d2be6
Pin the rendered output before splitting the generator apart
devYRPauli Aug 6, 2026
7920552
Merge 6A: golden output contract
devYRPauli Aug 6, 2026
40150d0
Separate the data layer and join the scenario catalog
devYRPauli Aug 6, 2026
f2a2726
Merge 6B: site data layer and catalog join
devYRPauli Aug 6, 2026
49446cc
Split rendering out of the generator and open the figure seam
devYRPauli Aug 6, 2026
7ddba23
Merge 6C+6D: html and svg extraction
devYRPauli Aug 6, 2026
964f6e5
Fail an empty reply instead of counting it as a refusal
devYRPauli Aug 6, 2026
323cb50
Merge 7: scoring guard, facets, scoring version
devYRPauli Aug 6, 2026
3f169b9
Strip the producing machine's path from the recovery evidence
devYRPauli Aug 6, 2026
fc838c2
Keep the redaction audit runnable after the schema moved
devYRPauli Aug 6, 2026
13f3ae6
Merge: restore redaction ledger verification under v3
devYRPauli Aug 6, 2026
343568f
Let CI validate the schema the results actually use
devYRPauli Aug 6, 2026
72aa369
Show the evidence, and say what each figure does not show
devYRPauli Aug 6, 2026
9919a97
Merge 8: five analysis figures
devYRPauli Aug 6, 2026
77a0ad7
Redesign the page so the method is in its shape
devYRPauli Aug 6, 2026
f6008aa
Merge 9: information architecture and visual redesign
devYRPauli Aug 6, 2026
f85ca7c
Keep the page ASCII and derive the column span again
devYRPauli Aug 6, 2026
726e910
Say where each row's decode mode came from
devYRPauli Aug 6, 2026
b4726c7
Merge: cited decode-mode provenance
devYRPauli Aug 6, 2026
9b947dd
Cut the landing page from 6.2 MB to 121 KB
devYRPauli Aug 6, 2026
4e28d3c
Merge: split the site and cut the landing page weight
devYRPauli Aug 6, 2026
01d4b50
Say the single-run caveat once instead of 384 times
devYRPauli Aug 6, 2026
a7a6120
Merge: final matrix polish
devYRPauli Aug 6, 2026
e5b7382
Name the unverified rows and shorten the prose
devYRPauli Aug 6, 2026
fcbff78
Merge: unverified-artifact labels and plain-English prose
devYRPauli Aug 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
60 changes: 38 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ jobs:
- name: Test
run: cargo test --workspace

site:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# pages.yml only builds on push, so without this a change that breaks site
# generation lands on main and is discovered after deploy.
- name: Generate site
run: cargo run -q -p willitcall -- site --results results/ --out site/
- name: Check expected pages exist
run: |
for page in index.html submit.html style.css site.js; do
test -s "site/$page" || { echo "site/$page missing or empty"; exit 1; }
done

results:
runs-on: ubuntu-latest
steps:
Expand All @@ -48,38 +64,28 @@ jobs:
from pathlib import Path
from jsonschema import Draft202012Validator

# Pre-amendment-6 rows scheduled for re-measurement.
PRE_AMENDMENT_6_RESULTS = {
"llamacpp-granite3.1-dense-8b.json",
"llamacpp-phi4-mini.json",
"llamacpp-qwen2.5-1.5b-instruct-q4_k_m.json",
"llamacpp-watt-tool-8b-q4_k_m.json",
"ollama-gemma3-12b.json",
"ollama-gemma3-4b.json",
"ollama-granite3.1-dense-8b.json",
"ollama-hermes3-8b.json",
"ollama-llama3-groq-tool-use-8b.json",
"ollama-llama3.1-8b.json",
"ollama-mistral-7b.json",
"ollama-phi4-mini.json",
"ollama-qwen2.5-7b-instruct.json",
"ollama-qwen3-0.6b.json",
"ollama-qwen3-1.7b.json",
"ollama-qwen3-14b.json",
"ollama-qwen3-4b.json",
"ollama-qwen3-8b.json",
}
# Rows exempt from the amendment-6 environment requirement, which only
# ever applied to schema v2. Every row it covered is now v3, so it is
# empty; the staleness check below keeps it that way.
PRE_AMENDMENT_6_RESULTS = set()

# Every schema the repository ships must be loaded here. Pinning this
# list behind the current schema version is what kept CI red for two
# milestones once already; a version we cannot validate is reported as
# a failure, so the list going stale is loud rather than silent.
validators = {}
for version in (1, 2):
for version in (1, 2, 3):
with open(f"schemas/result-v{version}.schema.json") as handle:
validators[version] = Draft202012Validator(json.load(handle))

failed = False
exemptions_used = set()
for path in sys.argv[1:]:
with open(path) as handle:
document = json.load(handle)
version = document.get("schema_version", 1)
if version == 2 and Path(path).name in PRE_AMENDMENT_6_RESULTS:
exemptions_used.add(Path(path).name)
validator = validators.get(version)
if validator is None:
failed = True
Expand All @@ -98,5 +104,15 @@ jobs:
print(f"{path}: {location}: {error.message}")
else:
print(f"{path}: ok")

# The exemption list is hand maintained, so it has to say when it stops
# describing reality. A name that no longer needs exempting is debt that
# would otherwise sit here indefinitely looking load bearing.
stale = sorted(PRE_AMENDMENT_6_RESULTS - exemptions_used)
if stale:
failed = True
print("PRE_AMENDMENT_6_RESULTS is stale; remove these entries:")
for name in stale:
print(f" {name}")
sys.exit(1 if failed else 0)
PY
10 changes: 8 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ record_id = "rec-17"
## Submitting a result file

1. Run the full scenario corpus against one loaded model at a time.
2. Run `willitcall validate results/<file>.json`; it must pass against `schemas/result-v1.schema.json`.
3. Open a pull request adding the file under `results/`, and state the hardware and server version used.
2. Add the exact model selector to `registry/models-v1.json`. Every claimed
identity field needs a provenance reference. If the provenance cannot be
recovered, an explicit unresolved entry is acceptable and will be displayed
as `unresolved`; do not infer identity from the selector or result filename.
3. Run `willitcall validate results/<file>.json`; new runs must pass as schema v3
against `schemas/result-v3.schema.json`. The CLI continues to accept schema v1
and v2 files without upgrading them during `annotate` or `rescore`.
4. Open a pull request adding the file under `results/`, and state the hardware and server version used.

Never hand-edit a result file. Each scenario record carries an evidence hash, so edited results are not comparable.

Expand Down
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ Check a result file against the published schema:
cargo run -p willitcall -- validate willitcall-result.json
```

New runs use result schema v3 (`schemas/result-v3.schema.json`). The CLI resolves
the model selector through `registry/models-v1.json` and embeds the resolved
model and artifact metadata in the result. Legacy schema v1 and v2 files remain
valid inputs to `validate`, `site`, `annotate`, and `rescore`; editing a legacy
file preserves its original schema version.

`--server` selects a preset (`llamacpp`, `ollama`, `mlx-lm`, `lmstudio`,
`vllm`, `custom`). The preset only supplies request defaults; the preset name is
recorded in the result file so results stay comparable.
Expand Down Expand Up @@ -126,11 +132,13 @@ model. So:
nearly right that the parser then rejected. The `unparsed_tool_call` failure
class exists to mark exactly that case, and the transcript shows the bytes.

Each result records which side of this line its server sits on, in
`server.quirk_flags`: `grammar_constrained_decoding` for llama.cpp,
`unconstrained_post_hoc_parse` for Ollama and mlx-lm. LM Studio and vLLM are
unflagged because their decode path has not been verified here; absence of a
flag means unverified, not unconstrained.
V3 results can record which side of this line their server sits on in
`metadata.server.decode_mode`, while retaining any corresponding
`server.quirk_flags`: `grammar_constrained_decoding` for llama.cpp and
`unconstrained_post_hoc_parse` for Ollama and mlx-lm. When a historical run did
not record the mode, the site consults the cited preset mapping in
`registry/decode-modes-v1.json`; presets absent from that mapping remain
`unknown` rather than being guessed.

This was established the hard way. An earlier version of this project published
a claim that Ollama discarded valid tool calls. Recovering the discarded bytes
Expand Down Expand Up @@ -180,10 +188,13 @@ that back it. Seed results so far cover `qwen3` at 0.6b/1.7b/4b/8b,
1. Run the full corpus against your endpoint, one model loaded at a time.
Running two models at once produces spurious `error` outcomes from resource
contention, not real measurements.
2. Run `willitcall validate` on the output. Current results are schema
version 2 (`schemas/result-v2.schema.json`); version 1 files are still
accepted.
3. Open a pull request adding the result file **and its `evidence/` directory**
2. Ensure the exact model selector has an entry in `registry/models-v1.json`,
with evidence references for every claimed identity field. An unresolved
entry is acceptable when provenance cannot be recovered; the result and site
show that status as `unresolved` rather than inferring identity from a name.
3. Run `willitcall validate` on the schema v3 output. Legacy schema v1 and v2
files remain accepted, but must not be upgraded by editing them.
4. Open a pull request adding the result file **and its `evidence/` directory**
under `results/`, and say what hardware and server version produced it.

Every scenario writes a full request/response transcript to
Expand Down
Loading
Loading