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.
What is wrong
src/oracle.rsemits a per-artifact manifest whoseschema_versionisquire.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
DerivationManifestserde struct withdeny_unknown_fieldsplus field-by-field assertions in threeintegration 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/v1toquire.codegen.derivation-manifest/v1would be a second bespoke schema wearing a new name, and therepository owner has ruled that out.
quoin change-assurance schemapublishes three shapes.ProofAttestationV1(
proof-attestation-v1.schema.json) is the right one: it binds one already-produced result file to areviewed 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.
ChangeAssuranceRecordV1is the wrong target —review_workflow,parent_digestandimpact_snapshotdescribe a change under review, not a generation event.The generator emits a body; Quoin seals it
quoin change-assurance seal-attestation --inputdocuments its input as "Proof-attestation body asJSON, without
digestand withoutretained_output", and "A body supplyingretained_outputordigestis 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_bodyalready doesfor the four existing obligations. The alternative, having the generator compute
digestandretained_outputitself, requires reimplementing Quoin's canonical-JSON encoding and its BLAKE3digest (verified:
digestandretained_output.digestare BLAKE3-256 over canonical JSON / rawbytes, 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
ProofAttestationV1minus exactly the two fields theshared CLI refuses to accept from a producer, and the migration proves that by running the real
quoin change-assurance seal-attestationover the emitted bytes in a test and validating the sealedresult against the packaged
proof-attestation-v1.schema.jsonbytes fetched fromquoin change-assurance schema.One attestation per artifact
An attestation binds one
retained_output. Oracles currently pack two outputs (Rust source andsource 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
schema_version: "quire.derivation-evidence/v1"schema_version: 1,record_type: "proof_attestation"record_idattestation_idrecorded_atobserved_atproducer.nametool.identity(agent-ix/quire-contract-codegen, which also carriesprovenance.repository)producer.source_revisiontool.version(the 40-hex form theimmutable_versionpattern admits)producer.executable_digesttool.configuration_digestproducer.invocation,parameters_digest,backend.kind,inputs[].content_digest,extensions.*(canonical profile, IR/runtime revision, clause id, source-byte limit),outputs[].schemacommand.argv— the parameters themselves rather than a digest of them, and the output schema named by path and digestenvironment.{target_triple, operating_system, toolchain, dependencies_digest},extensions.generator_source_dirty,extensions.generator_source_revision_availableenvironmentprovenance.candidate_revisioncandidate_revisionresult.statusresultoutputs[].uri,outputs[].content_digestretained_output, derived byseal-attestationfrom the artifact's own bytesrecord_digest— new, caller-suppliedWhat the shared shape does not carry
provenance.reviewersandprovenance.contribution_method. Neither shared schema has a field forthem; 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.summaryandresult.requirement_refs.ProofAttestationV1.resultis a four-value enumwith 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 fourobligations already carry them. Dropped from the artifact.
backend.reason— constant prose, not identity.--backend noneincommand.argvcarries thediscriminator. Dropped.
inputs[].schema.digestfor the canonical-expression input. The current value issha256("v1")— the digest of the profile name, not of any schema — so it is removed rather thanrehomed.
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 diagnosticwith no bundle. The six Interface-001 terminal states stay in
GenerationDiagnostic, which thischange does not touch, and the ten shared verification states stay demonstrated on the intake path.
resultis therefore derived aspassedrather than caller-supplied, which removes a way for acaller to state a result contradicting the artifact it accompanies.
Effect on generated artifacts
This changes generated output. Per generated bundle: the
manifests/*.jsonartifact is replaced byattestations/*.json; oracles gain a second one. Byte-identical regeneration is preserved — everyfield 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 ispre-release software and the crate is unpublished.
The generator's own implementation digest moves, because
src/oracle.rsis one of the blobs ithashes.
Scope
src/oracle.rs,src/harness.rs,src/strategy.rs,src/lib.rstests/oracle_generation.rs,tests/harness_generation.rs,tests/strategy_generation.rs,tests/shared_assurance.rsexamples/generation_conformance.rs,tests/fixtures/generated_boolean_oracle.goldenspec/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.mdassurance/change-assurance.json,CLAUDE.md,README.mdOut of scope:
schemas/generated-rust-oracle-v1.schema.jsonandschemas/oracle-source-map-v1.schema.json. They are domain output contracts describing the generatedartifacts 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
quire.derivation-evidence/v1.quoin change-assurance seal-attestation, and thesealed result validates against the packaged
proof-attestation-v1.schema.jsonbytes.make ciexits 0.