# `.verit` conformance corpus
The frozen contract every `.verit` **reader** is written against — the file-level
peer of `../vectors/` (which freezes the message wire format). Normative rules:
[File Format Specification §11](../../../docs/Architecture/VERIT%20-%20File%20Format%20Specification.md).
If you are porting a reader, this directory is your test suite. Pass all three
corpora and your reader is conformant.
```
files/
├── good/ valid files — open them and match the manifest
├── torn/ truncated mid-commit — recover the previous generation
└── bad/ one file per rejection rule — refuse each with a typed error
```
## `good/` — read it correctly
`manifest.tsv` carries one row per file:
| `file` | base name; the file is `<file>.verit` |
| `records` | live record count |
| `generation` | the commit counter the file resolves to |
| `next_record_id` | the id the next append would take |
`<file>.records.tsv` carries one row per record, in index order:
| `record_id` | the record's stable id (§6.1) |
| `schema_id` | its 128-bit schema id, lowercase hex, 32 chars |
| `dump_json` | the JSON the record yields **using only this file's bytes** |
That last column is the self-containment proof: no registry, no schema handed in
from outside. If your reader needs anything but the file to produce it, the file
format is not doing its job.
| `empty` | zero records is valid and canonical |
| `single` | the degenerate one-record file |
| `many` | 16 varying-length records — padding between 8-aligned starts |
| `mixed` | several schemas in one file, including two versions of one type |
| `inline` | records carrying their own inline schema (legal; readers must accept) |
| `defaults` | a schema with custom scalar defaults in its section |
| `appended` | generation 4 — two superseded footers to scan past |
| `removed` | ids with gaps, and dead bytes still in the file |
| `compacted` | generation reset to 1, ids **and** the id counter preserved |
`defaults` is the case a reader is most likely to get subtly wrong: the record
omits both defaulted fields, so their values must be recovered from the schema
in the file's own section. A reader that bounds its defaults decode with the
*record's* envelope length — which is `0` for a hash-only record — silently
skips them. That is exactly the bug the differential audit found in the COBOL
port, and nothing else in the corpus catches it.
`removed` and `compacted` are a pair, and the interesting one. `removed` has
five ids issued, three live (`1, 3, 5`), and the removed records' bytes still
sitting in the file — because removal unlinks, it does not erase (§8.2).
`compacted` proves the other half: ids survive compaction, so a consumer's
checkpoint stays valid, and `next_record_id` stays at 6 even though the record
holding id 5 is gone.
## `torn/` — recover, do not fail
Each file is a generation-3 commit truncated partway through, at a different
region: mid-record, at and inside the schema section, at and inside the index,
at and inside the footer, and one byte short of complete.
Every one must open at **generation 2 with 2 records**, and those records must
still decode. A reader that reports an error here has not implemented the
backward footer scan of §7.2; a reader that reports generation 3 has accepted a
commit that never completed.
| `file` | base name |
| `expected_generation` | the generation the reader must resolve to |
| `expected_records` | the live record count at that generation |
## `bad/` — refuse, do not crash
One file per rejection rule, named for the rule, with `manifest.tsv` giving the
rule in prose. Every one must be refused with a **typed error** — never a panic,
never an unbounded allocation, never a partial read (§10).
Four are worth calling out:
- **`forged-record-count`** claims `u32::MAX` records with a resealed footer
CRC, so it gets past the integrity check. It must be refused on *arithmetic* —
before anything is allocated. A reader that tries to allocate first dies here.
- **`descending-record-ids`** / **`duplicate-record-id`** break the sorted-index
invariant. If your reader accepts these, its `find_by_id` binary search returns
the *wrong record* rather than failing — a silent wrong answer, which is worse
than a crash. Validate ids on open.
- **`missing-schema`** points an index entry at a schema the section does not
carry. Accepting it means the file is not self-contained.
- **`vertc-container`** is a 0.1.0 `.vertc` image. It must be refused
structurally on magic, not partially misread.
## If you are writing a writer
Reading the corpus is only half the obligation. A writer must also pass the
**torn-write procedure** — four steps run against its own output, specified in
[File Format Specification §11.1](../../../docs/Architecture/VERIT%20-%20File%20Format%20Specification.md).
The corpus cannot test this for you: it checks that you can *read* a torn file,
not that the files *you write* roll back when torn.
Every port implements it under the names `writer/append`, `writer/remove`,
`writer/compact`, and `writer/torn-commit`, so the output lines are comparable
across implementations.
## Regenerating
```sh
cargo run -p verit --example files -- emit engine/tests/files
cargo run -p verit --example files -- verify engine/tests/files
```
`FileBuilder` is deterministic, so re-emitting an unchanged corpus produces
byte-identical output and any real change shows up as a reviewable diff. The
in-repo test `engine/tests/golden_files.rs` checks all three corpora on every
`cargo test`.