# neg-corpus v1.2.0 — rebase onto draft-zambo-aer1-09

Trigger: the vendor's ops lead ran v1.1.0 through the -09 conformance kit
oracle — **37/50 legacy cases agreed, 13 disagreed**, all -08→-09 drift, no
kit bugs. v1.2.0 rebinds every diverging case to the published -09 field
names and kit-verbatim `{check, reason}` strings.

**Final oracle score: 67/67 cases agree** (50/50 legacy + 17/17
commitment09), self-oracle 73/73 including differentials.
Transcript: `v120-verify.txt`. Oracle adapter: `kit-oracle.js`.

## Drift categories and fixes

### 1. id tier strictness — draft -09 §3

-09 §3 names two conformance tiers: the core rule accepts any lowercase
UUID; the UUID-v4/RFC-4122-variant rule applies only "where certification
or the disinterested tier is claimed". The -09 kit enforces it inside
`checkDisinterestedTier`, emitting `id is not a UUID v4 (strict profile)`.
A bare receipt with a non-v4 id therefore **verifies under the -09 core
rule** — the v1.1.0 strict-flag case pinned a reason the kit's default
profile never emits. Rebound: the case now carries the disinterested-tier
anchor that claims the strict profile. (`verify.js` gained the same check
for kit parity.)

### 2. schema version exactness — draft -09 §3

-09 §3 keeps `receipt_schema_version` as a non-empty string; the exact
`"0.3"` pin exists only in the certifier/strict profile
(`aer1-ecosystem/common/aer1_core.py`). The v1.1.0 `"0.2"` mutant is
core-valid under -09 → the mutation is dead as a core negative. Rebound
to a non-string schema (a -09 core violation), and the "0.2"-string
divergence moved to new differential `d06`.

### 3. raw-digest commitment vs §7.3 object — draft -09 §7.3

-09 defines the chain commitment as a JSON **object** in a `commitment`
member (`job_id`, `final_entry_digest`, `entry_count`, `published_at`).
The v1.1.0 docs carried a bare top-level `final_entry_digest` string —
not a -09 check, so the timelines verified clean. Both cases rebound to
`{timeline, commitment}` docs; reasons are now `chain-commitment(-09)`
verbatim.

### 4. workflow member-shape checks — kit `verifyWorkflow` scope

The -09 kit's `verifyWorkflow` implements only the §8.2 offline mechanics:
non-empty steps list, per-step `seq` integer + `receipt_id` string, seq
1..n in order, no duplicate `receipt_id`, `merkle_root` recompute. The
v1.1.0 member-shape mutations (`type`, `workflow_id`, `verify_url`,
`session_id`, step `tool`/`started_at`/`receipt_hash`) are invisible to
that oracle — 8 of the 9 mutants verified with **zero** failures, w05
emitted only the root-recompute failure. All nine rebound to single
-09-visible §8.1/§8.2 violations (names updated; old names below).

**Kit coverage gap, flagged:** §8 Table 2/Table 3 members (`type`,
`version`, `workflow_id`, `receipt_id`, `session_id`, `goal`, `status`,
`verify_url`, step `tool`/`receipt_hash`/`started_at`/`ended_at`/`status`)
are MUSTs in the draft but are *not* exercised by the -09 kit's workflow
check. Our `verify.js` still enforces them; they simply cannot be pinned
as corpus expect-sets while the kit oracle is the arbiter. Worth a kit
issue: member-shape negatives currently can't be corpus-verified.

## Changed cases — old expect → new expect

| v1.1.0 case | v1.2.0 case | Old expect (-08) | New expect (-09 verbatim) | § |
|---|---|---|---|---|
| r03-id-nonv4-strict | same | `core: id is not a UUID v4` (strict flag) | `disinterested-tier: id is not a UUID v4 (strict profile)` | §3 tiers via §10 |
| r05-schema-02-strict | r05-schema-nonstring | `core: receipt_schema_version is not 0.3` (strict flag) | `core: receipt_schema_version is not a non-empty string` | §3 |
| c11-commitment-mismatch | same | `final-entry-commitment: external commitment does not bind the final entry digest (expected f…64, recomputed 79b1…)` | `chain-commitment(-09): commitment final_entry_digest does not match the recomputed digest of the last entry` | §7.3 |
| c12-commitment-badfmt | same | `final-entry-commitment: external commitment is not a lowercase hex SHA-256 digest` | `chain-commitment(-09): commitment final_entry_digest is not 64 lowercase hex characters` | §7.3 |
| w01-missing-type | w01-steps-nonlist | `workflow: missing required member: type` | `workflow: workflow steps is not a non-empty list` | §8.2 |
| w02-wrong-type | w02-doc-not-object | `workflow: type is not "verifiable-workflow-receipt"` | `workflow: workflow is not a JSON object` | §8.2 |
| w03-wfid-nonuuid | w03-steps-reordered | `workflow: workflow_id is not a UUID` | `workflow: step seq values are not 1..5 in order (index 0 carries seq 2)` | §8.2 |
| w05-merkle-badfmt | w05-dup-receiptid-stale-root | `workflow: merkle_root is not a lowercase hex SHA-256 digest` + `…does not match the recomputed Section 8.1 root` | `workflow: duplicate receipt_id: bf88a036-…` + `…does not match the recomputed Section 8.1 root` | §8.2+§8.1 |
| w08-step-missing-tool | w08-step-seq-dup | `workflow: step 2 missing member: tool` + `step 2 tool is not a non-empty string` | `workflow: step seq values are not 1..5 in order (index 2 carries seq 2)` | §8.2 |
| w09-step-bad-ts | w09-step-seq-tail-skip | `workflow: step 1 started_at is not RFC 3339` | `workflow: step seq values are not 1..5 in order (index 4 carries seq 6)` | §8.2 |
| w10-verify-url-nonhttp | w10-steps-null | `workflow: verify_url is not an http(s) URL` | `workflow: workflow steps is not a non-empty list` | §8.2 |
| w12-step-bad-receipt-hash | w12-merkle-wrong-order | `workflow: step 1 receipt_hash is not hex SHA-256` | `workflow: merkle_root does not match the recomputed Section 8.1 root` | §8.1 |
| w14-sessionid-empty | w14-step-seq-zero | `workflow: session_id is not a non-empty string` | `workflow: step seq values are not 1..5 in order (index 3 carries seq 0)` | §8.2 |

**No case was rebased to `expect: []`.** Every v1.2.0 negative still kills
at least one -09 check; generation aborts otherwise (`generate.js`
self-oracle).

## Differentials

New: `d05-id-nonv4-tiers` (non-v4 id: -09 core accepts / strict rejects —
absorbs r03's receipt-level strict leg) and `d06-schema-non03` (`"0.2"`:
core accepts / strict pins 0.3 — absorbs r05's strict leg).

The original tripwires `d01`/`d02`/`d03` (trailing-newline id/provenance
tolerance, `+10:60` offset normalization) were **vindicated by the vendor**:
real leniencies, fixed on kit main — the kit now rejects all three at core
level. Their `expect_loose: []` pins are retained: they record the
pre-fix kit behavior reproduced by the kit snapshot this corpus was
verified against (/tmp/zambo @ `ad09360`). On the *fixed* kit, all three
documents now fail both legs — that is the intended outcome of the
differential, not a corpus defect.
