Skip to content

feat(ledgeringest): pin the published ledger-ingest v1 contract bundle - #447

Draft
tumberger wants to merge 1 commit into
mainfrom
jens/eng-608-pin-ledger-ingest-v1-contract
Draft

tumberger wants to merge 1 commit into
mainfrom
jens/eng-608-pin-ledger-ingest-v1-contract

Conversation

@tumberger

Copy link
Copy Markdown
Contributor

What

Vendors the published ledger-ingest v1 contract artifact (release 1.0.0) under docs/contracts/ledger-ingest/v1 and adds internal/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/ledgerfact already 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

  • Bundle integrity: manifest digest equals the pinned constant; every file matches its manifest digest; no unlisted file in the bundle.
  • Schema agreement: every corpus fixture validates or fails against the published batch schema exactly as declared (structural rejections fail the schema; semantic rules are server-owned and pass the schema layer).
  • Receipt canonicalization: recomputing every corpus receipt hash with the CLI's RFC 8785 (JCS) canonicalizer reproduces proof.receipt_hash byte-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/schema mirror.

Draft until the server-side v1 boundary is deployed.

🤖 Generated with Claude Code

…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 tumberger left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review findings:

  1. Fixture outcomes are not asserted (internal/ledgeringest/contract_test.go:98-103, 216-245). The test decodes status, code, pointer, and disposition, but only uses stage. 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.

  2. Legacy response status is inconsistent (fixtures/manifest.json, README.md, openapi.json). All compatibility fixtures expect 201, while the README says success is 200 only and OpenAPI defines only 200 for 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.

  3. OpenAPI is not validated (contract_test.go:173-182). The four standalone schemas compile, but openapi.json is 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant