# EXPECTED — aer1verify.lua verdicts

Ground truth produced by the canonical independent verifier
(`node verify.js <dir>`), which the Lua program ports check-for-check
(same port lineage as `swift/aer1verify.swift`). Two runs are exercised
below; `results.txt` holds the captured output.

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

Command:

    lua5.4 deliverables/aer1/lua/aer1verify.lua \
        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 (73 manifest cases, must-fail oracle)

Command:

    lua5.4 run_negcorpus.lua deliverables/aer1/neg-corpus

Replays manifest.json asserting EXACT {check,reason} failure sets.
Expected: **73/73 conform** — identical to `node run.js` on the same
manifest. (Corpus v1.2.1 repinned the d01/d02/d03 `expect_loose` legs to
what verify.js — and the shipped kit-snapshot's `check()` — actually
emit; they previously froze the pre-fix kit's trailing-newline/offset
leniencies. See DIFF-FIX.md.)

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

- Directory scanning is recursive; verify.js read only top-level *.json.
  Required so the nested test-vectors layout works as a single target.
  For flat dirs (v1.3.0) behavior is identical. (Same call the Swift
  port made.)
- Parse failure reason is the constant "not valid JSON" (verify.js
  appends the JS error message, which has no Lua equivalent; matches
  the Swift port's choice).
- `entry_count: <digits>.<digits>` is marked `{"__float__": n}` at parse
  time, mirroring the -10 kit vector loader's textual rewrite (the same
  semantic the Java port keeps via number lexemes). verify.js's own CLI
  loader does no marking, so this port rejects `entry_count: 5.0` where
  `node verify.js` would accept it — a deliberate conformance fix, not
  an accident; `seq: 2.0` remains an accepted integer-valued number.
- verify.js's unreadable-file crash becomes a FAIL line (fail closed).
- `--json` output is a pretty-printed report; field order is fixed
  (file, kind, ok, failures→check, reason) but whitespace is not
  byte-identical to JSON.stringify(report, null, 1).
- `--selftest` is an added flag (pinned vectors for the vendored
  SHA-256/base64/UTF-8/JSON primitives); it is consumed before the
  target list like the other flags.

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