# Compat-mode KNOWN_DIFFS
Tracker for documented compat-mode differences between trybuild and
lihaaf — the catalogue that informs which pilot fixtures land in
`excluded_fixtures` vs which qualify for the §5 mismatch ceiling.
Maintained alongside `compat/baseline.toml`: a baseline ceiling entry
without a corresponding KNOWN_DIFFS row is a red flag in review.
## Workflow
1. A pilot run produces a `compat-report.json` envelope with
`results.mismatch_count > 0`.
2. The pilot owner reviews each entry in `mismatch_examples` and
classifies it:
- **Tracked here (KNOWN_DIFFS)** — the divergence is understood and
either acceptable as-is or queued for a lihaaf fix.
- **Excluded** — the fixture exercises a trybuild surface lihaaf
does not yet implement; added to `excluded_fixtures` in the
envelope and listed in the per-crate excluded section below.
3. The PR adjusts `compat/baseline.toml` to the post-classification
mismatch count (only shrinking; growth requires explicit review per
§5).
## Tracked divergences
### Wrapper-vs-per-fixture totals correlation (v0.2 work)
**Symptom:** `results.baseline.pass + results.baseline.fail` reports
per-libtest-test counts (one wrapper-function per `#[test] fn ui()`),
while `results.lihaaf.pass + results.lihaaf.fail` reports per-fixture
counts (one per `.rs` file under `tests/ui/`). The §5 totals rule's
"equal unless `excluded_fixtures` accounts for the delta" doesn't hold
for the typical trybuild usage pattern where ONE wrapper test invokes
N internal fixtures.
**Affects:** Any pilot crate using the trybuild wrapper pattern
(`t.pass(...)` / `t.compile_fail(...)` inside a `#[test]` fn). Most
existing trybuild adopters.
**Workaround for v0.1.0-beta.4:** Pilot enrollment in
`compat/baseline.toml` is operationally on hold for crates with the
wrapper pattern. Crates whose trybuild tests expose one libtest test
per fixture (uncommon) work today.
**Resolution path (v0.2):** The conservative parser needs
wrapper-aware semantics — recognize the libtest wrapper line as a
wrapper (not a per-fixture verdict), and either skip it or correlate
it to the union of internal fixtures. Spec revision may be required at
`docs/compatibility-plan.md:239-244` to clarify the per-side totals
contract.
Tracked since `1fced5c` (round-2 fixups; this issue surfaced in
round-2 adversarial review).
### Windows read-only artifact cleanup (v0.2 work)
**Symptom:** `std::fs::remove_file` on Windows does NOT clear the
read-only file attribute before unlinking. Compat-converted artifacts
that the inner build leaves with the read-only attribute set under
`<compat_root>/target/lihaaf-compat-converted/` will fail the cleanup
walker's step 1 with `PermissionDenied`. The round-4 cascade fix
(commit `4e6c5bc`) now correctly falls through PermissionDenied to
step 2 (`RemoveDirectoryW`) for read-only NON-EMPTY directories, but
step 1's `DeleteFileW` and step 3's `remove_dir_all` do NOT clear
the file-level read-only attribute on individual files within a
recursive walk.
**Affects:** Windows adopters whose trybuild fixtures emit
read-only artifacts. Uncommon for typical UI tests; may surface for
code-generation or template-rendering proc-macros that mark output
read-only. The compat-converted tree itself is generated by lihaaf's
`fixture_convert`, which does NOT set read-only attributes — so this
only bites when the inner build produces read-only output.
**Workaround for v0.1.0-beta.4:** Rare in practice. Adopters who hit
this can manually clear read-only attributes via PowerShell
`Get-ChildItem -Recurse | Set-ItemProperty -Name IsReadOnly -Value
$false` on the residue directory after a failed run, then rerun.
**Resolution path (v0.2):** Add a Windows-side attribute-clearing
walker before unlink in `crate::util::remove_path_race_free`. Use
`SetFileAttributesW` with the appropriate clear-mask before
`RemoveDirectoryW` / `DeleteFileW` (the same dance Cargo itself
does in `cargo/src/cargo/util/paths.rs` for its target-dir
cleanup).
Tracked since `e851d94` (round-4 adversarial review counter-signal
from Gemini round-3; symptom predates that commit but the cascade
cleanup landed in round-4).
### `compat/baseline.toml` schema versioning (v0.2 work)
**Symptom:** `compat/baseline.toml` has no `schema_version` field.
The §3.3 envelope and the baseline sidecar both carry an explicit
`schema_version: u32`; the baseline ceiling table does not. The
current `parse_baseline` silently ignores unknown keys, so a v0.2
baseline.toml with new keys would parse successfully on a v0.1.0-
beta.4 binary, losing the new keys' enforcement without operator
warning.
**Affects:** Future v0.2 schema-extension work (e.g., asymmetric
per-side `expected_exit_code`, wrapper-aware totals rules, per-crate
exclusion rules, fixture-level mismatch ceiling overrides).
**Workaround for v0.1.0-beta.4:** Not needed — table is empty; no
enrolled pilots. The current toolchain rev moves forward in lockstep
with the binary, so schema-skew between toolchain and binary is not
yet possible.
**Resolution path (v0.2):** Add an optional top-level
`schema_version` integer key (default 1 when absent). When v0.2
introduces incompatible keys, bump to 2 and reject lower versions
with a directed diagnostic that names the missing key and points to
the binary version that supports it.
Tracked since `e851d94` (round-4 adversarial review counter-signal
from strict-swe Opus and Gemini).
Each row uses the shape:
> **`<area>`** — `<short summary>`. Tracked since `<commit/PR>`.
> Plan: `<fix in lihaaf vNN | accept | excluded>`.
## Per-crate excluded fixtures
_None yet._
Each crate gets a subsection listing the fixtures it ships in
`excluded_fixtures` and why.
## Schema notes
- This is a hand-curated document — there is no parser. The §5 gate
reads `compat/baseline.toml` (machine-readable) and the
`excluded_fixtures` field in the envelope; this file is the human
audit trail tying them together.
- `docs/compatibility-plan.md` §6 is the upstream spec for this
workflow; this file is the in-tree instance.