Context
#286 now provides XSD-derived XML parser contracts and records the exact XSD bytes used in run provenance.
Recent work in 4ca1cc6 also resolves multi-file schema sets through:
xs:include
xs:import
xs:redefine
and records every resolved schema document as a hashed input artifact in the signed run manifest.
That gives excellent after-the-fact provenance.
The remaining gap is before-the-run enforcement.
Problem
A publisher can replace the bytes behind an unchanged path or URL:
https://registry.example/schema/company.xsd
while the Duckle pipeline itself remains unchanged.
The next run then derives a different parser contract.
Today the run manifest can tell us afterwards:
previous XSD hash != current XSD hash
but the production ingestion may already have parsed and published data using the new contract.
For recurring registry feeds, I would like a mode where changing the authoritative parser contract requires explicit acceptance.
Desired behavior
Treat the complete resolved XSD dependency set as one versioned parser contract.
Conceptually:
root XSD
↓
resolve include/import/redefine closure
↓
canonical dependency set
↓
hash/fingerprint
↓
compare with accepted parser contract
A useful policy could be:
xsd:
uri: schemas/company.xsd
changePolicy: fail
with possible values:
Backward-compatible default behavior can remain allow.
Dependency-set identity
The fingerprint should cover the resolved schema closure, not only the root document.
For example:
canonical URI/path
SHA-256
resolved namespace, where relevant
for every document.
This catches:
root schema changed
import changed
include changed
dependency added
dependency removed
dependency graph changed
even when the configured root URI remains identical.
Fail before parsing the large source
With:
the check should happen after the schema set is resolved and hashed but before Duckle starts parsing the large XML input or performs downstream side effects.
For a 30 GB registry feed the desired flow is:
resolve XSD set
↓
compare contract fingerprint
↓
changed?
yes -> stop
no -> start large XML parse
A parser-contract mismatch should not advance watermarks, baselines, publication state, or other deferred-success state.
Explain the change
Machine-readable output should identify what changed.
For example:
{
"code": "xsd_contract_changed",
"root": "schemas/company.xsd",
"previous": "...",
"current": "...",
"changes": [
{
"uri": "schemas/common-types.xsd",
"change": "modified",
"previous_sha256": "...",
"current_sha256": "..."
},
{
"uri": "schemas/new-types.xsd",
"change": "added",
"sha256": "..."
}
]
}
Where Duckle can cheaply derive it, also showing the resulting column/schema difference would be useful:
+ legal_form_code VARCHAR
turnover DECIMAL(18,2) -> DECIMAL(20,2)
address required -> optional
but dependency-level drift detection is sufficient for v1.
Explicit acceptance
Provide an explicit way for an operator or authorized agent to make a changed parser contract the new accepted version.
Conceptually:
duckle schema inspect <pipeline>
duckle schema accept <pipeline>
or XSD-specific equivalents.
The exact command naming is not important.
Important semantics:
- acceptance is explicit;
- acceptance is audited;
- a failed run never automatically accepts the observed schema;
- production policy can forbid agents from accepting a new parser contract;
- the previously accepted fingerprint remains available for comparison.
This is similar in spirit to manually accepting a new qa.baseline.
Run provenance
A run should record both:
accepted parser-contract fingerprint
observed/resolved XSD dependency hashes
so it is possible to prove that the run used exactly the contract that was approved.
The existing signed artifact records can remain the source of the individual XSD hashes.
Remote/offline behavior
This should compose with #288 and the network policy.
In a pinned/offline production environment:
accepted XSD dependency set
-> exact known bytes
should be usable without silently fetching a new version from the publisher.
A remote schema changing should never be automatically accepted merely because the URL remained the same.
Scope
This does not require:
- full XSD validation;
- implementing the complete XML Schema standard;
- semantic compatibility analysis between arbitrary XSD versions;
- a hosted schema registry.
A first implementation can simply detect:
accepted dependency fingerprint != observed dependency fingerprint
and apply allow | warn | fail.
That already provides the important safety property.
Why this matters
For recurring official-register ingestion, the XSD is effectively part of the parser implementation.
The production invariant should therefore be:
Pipeline code and parser contract cannot change independently without the change being visible and, when configured, explicitly approved.
That makes registry ingestion considerably more reproducible and safer for unattended or agent-managed pipelines.
Context
#286 now provides XSD-derived XML parser contracts and records the exact XSD bytes used in run provenance.
Recent work in
4ca1cc6also resolves multi-file schema sets through:and records every resolved schema document as a hashed input artifact in the signed run manifest.
That gives excellent after-the-fact provenance.
The remaining gap is before-the-run enforcement.
Problem
A publisher can replace the bytes behind an unchanged path or URL:
while the Duckle pipeline itself remains unchanged.
The next run then derives a different parser contract.
Today the run manifest can tell us afterwards:
but the production ingestion may already have parsed and published data using the new contract.
For recurring registry feeds, I would like a mode where changing the authoritative parser contract requires explicit acceptance.
Desired behavior
Treat the complete resolved XSD dependency set as one versioned parser contract.
Conceptually:
A useful policy could be:
with possible values:
Backward-compatible default behavior can remain
allow.Dependency-set identity
The fingerprint should cover the resolved schema closure, not only the root document.
For example:
for every document.
This catches:
even when the configured root URI remains identical.
Fail before parsing the large source
With:
the check should happen after the schema set is resolved and hashed but before Duckle starts parsing the large XML input or performs downstream side effects.
For a 30 GB registry feed the desired flow is:
A parser-contract mismatch should not advance watermarks, baselines, publication state, or other deferred-success state.
Explain the change
Machine-readable output should identify what changed.
For example:
{ "code": "xsd_contract_changed", "root": "schemas/company.xsd", "previous": "...", "current": "...", "changes": [ { "uri": "schemas/common-types.xsd", "change": "modified", "previous_sha256": "...", "current_sha256": "..." }, { "uri": "schemas/new-types.xsd", "change": "added", "sha256": "..." } ] }Where Duckle can cheaply derive it, also showing the resulting column/schema difference would be useful:
but dependency-level drift detection is sufficient for v1.
Explicit acceptance
Provide an explicit way for an operator or authorized agent to make a changed parser contract the new accepted version.
Conceptually:
or XSD-specific equivalents.
The exact command naming is not important.
Important semantics:
This is similar in spirit to manually accepting a new
qa.baseline.Run provenance
A run should record both:
so it is possible to prove that the run used exactly the contract that was approved.
The existing signed artifact records can remain the source of the individual XSD hashes.
Remote/offline behavior
This should compose with #288 and the network policy.
In a pinned/offline production environment:
should be usable without silently fetching a new version from the publisher.
A remote schema changing should never be automatically accepted merely because the URL remained the same.
Scope
This does not require:
A first implementation can simply detect:
and apply
allow | warn | fail.That already provides the important safety property.
Why this matters
For recurring official-register ingestion, the XSD is effectively part of the parser implementation.
The production invariant should therefore be:
That makes registry ingestion considerably more reproducible and safer for unattended or agent-managed pipelines.