Conversation
…e (ENG-608) Vendors the ledger-ingest v1 contract artifact (release 1.0.0) — OpenAPI document, JSON Schema 2020-12 record schemas, and the conformance fixture corpus — under docs/contracts/ledger-ingest/v1, pinned by a single manifest digest in the new internal/ledgeringest package. Tests prove, from the Go side: - bundle integrity: every file matches the manifest digests and no unlisted file hides in the bundle (single-constant pin, same pattern as the decision-fact corpus in internal/ledgerfact); - schema agreement: every corpus fixture validates (or fails) against the published batch schema exactly as its declared expectation says, including structural-vs-semantic rejection stages; - receipt canonicalization: recomputing every corpus receipt hash with the CLI's RFC 8785 (JCS) canonicalizer reproduces the published proof.receipt_hash byte-for-byte. This is the first step of the producer migration: no wire behavior changes yet. Follow-ups move the exporter onto typed clean-v1 records and retire the legacy field names. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tumberger
left a comment
There was a problem hiding this comment.
Review findings:
-
Fixture outcomes are not asserted (
internal/ledgeringest/contract_test.go:98-103, 216-245). The test decodesstatus,code,pointer, anddisposition, but only usesstage. Replay conflicts, legacy outcomes, and error metadata therefore pass as long as the request is structurally valid/invalid. Please either run these fixtures through the server adapter or add fixture-integrity assertions that validate the declared outcome and replay sequence. -
Legacy response status is inconsistent (
fixtures/manifest.json,README.md,openapi.json). All compatibility fixtures expect201, while the README says success is200only and OpenAPI defines only200for this route. The currently-unused status metadata lets this drift. Please choose one response contract, update all three artifacts, and make the test assert it. -
OpenAPI is not validated (
contract_test.go:173-182). The four standalone schemas compile, butopenapi.jsonis never parsed or reference-resolved. A broken request/response$ref(or stale embedded component) remains byte-pinned but unusable. Please add a hermetic OpenAPI 3.1 parse/reference-resolution check and verify its request/response schemas agree with the standalone artifacts.
Non-blocking follow-up: the receipt JCS test skips all invalid/compatibility fixtures, so it does not prove that each semantic-invalid fixture is invalid for only its declared reason. Verify valid hashes in chain/session/signature-negative cases and explicitly assert the intentional hash-mismatch case.
What
Vendors the published ledger-ingest v1 contract artifact (release 1.0.0) under
docs/contracts/ledger-ingest/v1and addsinternal/ledgeringest, which pins the whole bundle with a single manifest digest and proves the contract from the Go side.Closes the approach gap that closed #437: instead of a hand-maintained CLI-side schema mirror, the CLI consumes the published server-owned artifact verbatim and tests against it — the same verbatim-copy + pinned-digest pattern
internal/ledgerfactalready uses for the decision-fact corpus, upgraded to a one-constant pin because the bundle manifest digests every file.Bundle contents
openapi.json— the v1 HTTP API (POST /api/v1/authorization-ledger/batches, bearer install token, RFC 9457 problem responses).ledger-batch|session-record|action-record|receipt-record.schema.json— JSON Schema 2020-12; validators must enable format assertion.fixtures/— 41-case conformance corpus (valid / invalid / compatibility / replay) with declared expected status, problem code, JSON-Pointer, and rejection stage.manifest.json— release + SHA-256 for every file;README.md— limits, version meanings, canonicalization and chain rules.Tests
structuralrejections fail the schema;semanticrules are server-owned and pass the schema layer).proof.receipt_hashbyte-for-byte — the cross-language agreement the contract's tamper-evidence rests on.Scope
No wire behavior changes: the daemon still emits the current form. Follow-ups (tracked in ENG-608) move the exporter onto typed clean-v1 records with a one-time cutover, then delete the legacy serializer and the old
docs/schemamirror.Draft until the server-side v1 boundary is deployed.
🤖 Generated with Claude Code