Skip to content

Emit the shared ProofAttestationV1 shape instead of the deprecated derivation-evidence manifest #20

Description

@kreneskyp

What is wrong

src/oracle.rs emits a per-artifact manifest whose schema_version is quire.derivation-evidence/v1
— the deprecated PGM-01 evidence envelope. Its schema was deleted in #18 (merged as 8f2a6fb), so the
format is now emitted with no published contract at all: the shape is held only by the
DerivationManifest serde struct with deny_unknown_fields plus field-by-field assertions in three
integration tests.

This repository is the last of the eight in the campaign still producing that format. The other seven
stopped during their migrations.

What replaces it

The shared shape, not a renamed local one. Renaming quire.derivation-evidence/v1 to
quire.codegen.derivation-manifest/v1 would be a second bespoke schema wearing a new name, and the
repository owner has ruled that out.

quoin change-assurance schema publishes three shapes. ProofAttestationV1
(proof-attestation-v1.schema.json) is the right one: it binds one already-produced result file to a
reviewed record, a candidate revision, a proof obligation, a command, a tool, an environment, a time
and a result. That is exactly what the manifest was trying to say about one generated artifact.

ChangeAssuranceRecordV1 is the wrong target — review_workflow, parent_digest and
impact_snapshot describe a change under review, not a generation event.

The generator emits a body; Quoin seals it

quoin change-assurance seal-attestation --input documents its input as "Proof-attestation body as
JSON, without digest and without retained_output", and "A body supplying retained_output or
digest is refused." Measured against quoin 0.23.1: supplying either is exit 2.

So the generator emits the body and Quoin seals it. That is the campaign's own rule — the generator
produces, the chain seals — and it is what scripts/assurance_chain.py::attestation_body already does
for the four existing obligations. The alternative, having the generator compute digest and
retained_output itself, requires reimplementing Quoin's canonical-JSON encoding and its BLAKE3
digest (verified: digest and retained_output.digest are BLAKE3-256 over canonical JSON / raw
bytes, not SHA-256) inside a domain crate — which is precisely the local identity framework
FR-006-AC-6 forbids.

The emitted body is not a new schema. It is ProofAttestationV1 minus exactly the two fields the
shared CLI refuses to accept from a producer, and the migration proves that by running the real
quoin change-assurance seal-attestation over the emitted bytes in a test and validating the sealed
result against the packaged proof-attestation-v1.schema.json bytes fetched from
quoin change-assurance schema.

One attestation per artifact

An attestation binds one retained_output. Oracles currently pack two outputs (Rust source and
source map) into one manifest, so oracle generation emits two attestation bodies, one per
artifact, each with its own proof_id. Harness and strategy generation emit one.

Field mapping

deprecated manifest ProofAttestationV1
schema_version: "quire.derivation-evidence/v1" schema_version: 1, record_type: "proof_attestation"
record_id attestation_id
recorded_at observed_at
producer.name tool.identity (agent-ix/quire-contract-codegen, which also carries provenance.repository)
producer.source_revision tool.version (the 40-hex form the immutable_version pattern admits)
producer.executable_digest tool.configuration_digest
producer.invocation, parameters_digest, backend.kind, inputs[].content_digest, extensions.* (canonical profile, IR/runtime revision, clause id, source-byte limit), outputs[].schema command.argv — the parameters themselves rather than a digest of them, and the output schema named by path and digest
environment.{target_triple, operating_system, toolchain, dependencies_digest}, extensions.generator_source_dirty, extensions.generator_source_revision_available environment
provenance.candidate_revision candidate_revision
result.status result
outputs[].uri, outputs[].content_digest retained_output, derived by seal-attestation from the artifact's own bytes
— record_digest — new, caller-supplied

What the shared shape does not carry

  • provenance.reviewers and provenance.contribution_method. Neither shared schema has a field for
    them; review attribution lives in the ix-flow decision event a verification receipt binds. A
    generated file asserting reviewer logins is a claim the generator has no basis for, and NFR-002-AC-4
    already forbids release-approval claims. Dropped.
  • result.summary and result.requirement_refs. ProofAttestationV1.result is a four-value enum
    with no free-text or reference slot. Requirement references belong on the record's
    definition.proof_obligations[].obligation_ids, which is where this repository's own four
    obligations already carry them. Dropped from the artifact.
  • backend.reason — constant prose, not identity. --backend none in command.argv carries the
    discriminator. Dropped.
  • inputs[].schema.digest for the canonical-expression input. The current value is
    sha256("v1") — the digest of the profile name, not of any schema — so it is removed rather than
    rehomed.

Outcome vocabulary

Unchanged and uncollapsed. A manifest is only ever emitted on success: both construction sites
hard-code GenerationTerminalState::Generated, and every other terminal state returns a diagnostic
with no bundle. The six Interface-001 terminal states stay in GenerationDiagnostic, which this
change does not touch, and the ten shared verification states stay demonstrated on the intake path.
result is therefore derived as passed rather than caller-supplied, which removes a way for a
caller to state a result contradicting the artifact it accompanies.

Effect on generated artifacts

This changes generated output. Per generated bundle: the manifests/*.json artifact is replaced by
attestations/*.json; oracles gain a second one. Byte-identical regeneration is preserved — every
field is a build-time constant or a function of the request — but the bytes themselves change, so
every downstream consumer of a manifests/ path re-derives. There are no consumers: this is
pre-release software and the crate is unpublished.

The generator's own implementation digest moves, because src/oracle.rs is one of the blobs it
hashes.

Scope

  • src/oracle.rs, src/harness.rs, src/strategy.rs, src/lib.rs
  • tests/oracle_generation.rs, tests/harness_generation.rs, tests/strategy_generation.rs,
    tests/shared_assurance.rs
  • examples/generation_conformance.rs, tests/fixtures/generated_boolean_oracle.golden
  • spec/interface/interface-001-codegen-api.md, spec/functional/FR-001, spec/functional/FR-002,
    spec/nonfunctional/NFR-002, spec/assurance/MP-001, spec/test/TC-001, spec/test-matrix.md
  • assurance/change-assurance.json, CLAUDE.md, README.md

Out of scope: schemas/generated-rust-oracle-v1.schema.json and
schemas/oracle-source-map-v1.schema.json. They are domain output contracts describing the generated
artifacts themselves, not evidence about them, and they stay.

Pins unchanged: quire-cli 0.31.0, quoin 0.23.1, ix-flow 0.0.4, engineering-assurance 0.2.0.

Acceptance

  • Nothing in the repository emits or names quire.derivation-evidence/v1.
  • Every emitted attestation body is accepted by quoin change-assurance seal-attestation, and the
    sealed result validates against the packaged proof-attestation-v1.schema.json bytes.
  • No coverage row becomes unbacked.
  • make ci exits 0.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions