# EXPECTED — aer1verify.rb verdicts

Ground truth produced by the canonical independent verifier
(`node verify.js <dir>`), which this Ruby program ports check-for-check.
Identical per-file verdicts and first-failing checks as the Swift port's
EXPECTED.md (same corpus, same reference behavior).

## Run 1 — kit-snapshot/aer-1/test-vectors (30 files, recursive)

Command:

    ruby deliverables/aer1/ruby/aer1verify.rb \
        deliverables/aer1/kit-snapshot/aer-1/test-vectors

Files are processed sorted by path (`-` sorts before `/`, so
`invalid-profile/` precedes `invalid/`). Expected output, one line per
file:

| file | verdict | first failing check (FAIL lines only) |
|------|---------|----------------------------------------|
| anchored/anchored-bad-id-version.json | FAIL | id is not a UUID v4 (strict profile) |
| anchored/anchored-bad-leaf-hash.json | FAIL | anchor.leaf_hash does not match canonical_bytes |
| anchored/anchored-bad-timestamp.json | FAIL | anchor.anchored_at is not a valid calendar date/time |
| anchored/anchored-missing-proof.json | FAIL | anchor has members other than log/leaf_hash/anchored_at/proof (+1 more) |
| anchored/anchored-valid.json | PASS | |
| invalid-profile/missing-inputs.json | PASS | (core-valid; this is the intentional profile boundary — it fails only under `--profile`) |
| invalid/bad-created-at.json | FAIL | created_at is not RFC 3339 |
| invalid/bad-id.json | FAIL | id is not a lowercase UUID |
| invalid/bad-offset-range.json | FAIL | created_at is not a valid calendar date/time |
| invalid/bad-provenance.json | FAIL | provenance_class is not a known class |
| invalid/bad-verification-status.json | FAIL | verification_status is not verified |
| invalid/base64-unpadded.json | FAIL | canonical_bytes is not valid base64 |
| invalid/bytes-not-base64.json | FAIL | canonical_bytes is not valid base64 |
| invalid/feb29-1900.json | FAIL | created_at is not a valid calendar date/time |
| invalid/feb29-non-leap.json | FAIL | created_at is not a valid calendar date/time |
| invalid/hash-bad-format.json | FAIL | output_hash is not sha256: + lowercase hex digest |
| invalid/hash-mismatch.json | FAIL | output_hash does not match sha256(canonical_bytes) |
| invalid/id-trailing-newline.json | FAIL | id is not a lowercase UUID |
| invalid/impossible-date.json | FAIL | created_at is not a valid calendar date/time |
| invalid/missing-output-hash.json | FAIL | missing required member: output_hash |
| invalid/non-utf8-bytes.json | FAIL | canonical_bytes is not valid UTF-8 |
| invalid/provenance-trailing-newline.json | FAIL | provenance_class is not a known class |
| invalid/tool-empty-name.json | FAIL | tool is not an object with name/version/scope strings |
| invalid/tool-not-object.json | FAIL | tool is not an object with name/version/scope strings |
| invalid/uppercase-id.json | FAIL | id is not a lowercase UUID |
| valid/feb29-leap.json | PASS | |
| valid/fraction-one-digit.json | PASS | |
| valid/receipt-01.json | PASS | |
| valid/receipt-02.json | PASS | |
| valid/receipt-03-live.json | PASS | |

Expected tail:

    CONFORMANCE: 7/30
    exit code: 1

Note on missing-inputs.json: it is intentionally core-valid (the kit's
profile-boundary fixture). verify.js — and therefore this port — reports
PASS for it in default mode; it is rejected only when the
reference-producer profile is enabled (`--profile`). Do not "fix" this to
match the directory name; the boundary is intentional. (Enabling
`--profile` would also fail the legitimate valid/receipt-03-live.json,
whose canonical payload omits "inputs".)

## Run 2 — kit-snapshot/aer-1/frozen-vectors/v1.3.0 (18 files, flat)

Same format as test-vectors (receipt fixtures + a MANIFEST.txt carrying
sha256 checksums only). Expected:

| file | verdict |
|------|---------|
| anchored-bad-leaf-hash.json | FAIL |
| anchored-bad-timestamp.json | FAIL |
| anchored-missing-proof.json | FAIL |
| anchored-valid.json | PASS |
| bad-created-at.json | FAIL |
| bad-id.json | FAIL |
| bad-provenance.json | FAIL |
| bytes-not-base64.json | FAIL |
| hash-bad-format.json | FAIL |
| hash-mismatch.json | FAIL |
| impossible-date.json | FAIL |
| missing-inputs.json | PASS (profile boundary, see note above) |
| missing-output-hash.json | FAIL |
| non-utf8-bytes.json | FAIL |
| receipt-01.json | PASS |
| receipt-02.json | PASS |
| receipt-03-live.json | PASS |
| tool-not-object.json | FAIL |

Expected tail:

    CONFORMANCE: 5/18
    exit code: 1

## Run 3 — neg-corpus manifest replay (auxiliary)

Replaying `neg-corpus/manifest.json` (67 cases + 6 differentials) through
`verify_document` produces byte-identical output to `node run.js` on
verify.js: **70/73 conform**. The three non-conforming differentials
(d01-id-trailing-newline, d02-offset-min-60, d03-prov-trailing-newline)
have stale `expect_loose` entries predating draft -10's removal of the
trailing-newline tolerance — verify.js itself reports the same diffs, so
the Ruby port reproduces reference behavior exactly.

## Judgment calls vs verify.js (documented, not silently changed)

- Directory scanning is recursive; verify.js reads only top-level
  `*.json`. Required so the nested test-vectors layout works as a single
  target (same call the Swift port made). For flat dirs (v1.3.0)
  behavior is identical.
- Parse failure reason is `not valid JSON: <message>` like verify.js,
  but the message text is Ruby's parser error, not V8's (only the
  "not valid JSON" verdict matters for scoring).
- Unpaired `\uXXXX` surrogate escapes are rewritten to U+FFFD before
  parsing (Ruby's json gem rejects them; JS accepts). See RUBY-PORT.md.
- `--json` output uses Ruby's `JSON.pretty_generate` (2-space indent)
  rather than JS `JSON.stringify(_, _, 1)`.
- verify.js's unreadable-file crash becomes a FAIL line (fail closed),
  matching the Swift port's documented choice.

Everything else — check order, failure strings, strict/chain06/profile/
anchor flags, auto kind detection — is a verbatim port.
