Executive Summary

A machine learning dataset travels with a descriptor, a document that states in machine-readable form what the data is, where it came from and how to load it. Croissant is the standard for that document, and since version 1.1, published in January of this year, it carries conditions of use as well. There is now a place to write down that the data is for non-commercial purposes only, or that it may be used only after it has passed quality control. This article looks at an arXiv paper posted on September 17 that supplies the procedure by which a machine reads what sits in that place and actually refuses a request.

The paper names five gaps. Croissant 1.1 settled where a condition is written and left open how a condition is evaluated, what evaluation may cost, what happens to a condition an implementation cannot check, whether anything is recorded about what was checked, and how all of this composes with the authority that governs the caller. The author closes the operator set at five and states each operator's decision procedure in the paper itself, which supplies half of that list. Across three descriptors that gated a real analysis pipeline, decisions taken from the profile document matched the existing gate's records exactly. Across a corpus generated from the profile's grammar, 552 decision records agreed three ways.

Sections 1 through 5 follow what the paper reports. Section 4 on evaluation cost and Section 5 on the two authorities both mix measurement with claims the author withdraws and with a caller side the author describes as modelled, so each of those is marked where it appears. Section 6 reads the structure from the AI-Ready Data side, and that reading belongs to this article.

Key figures

Source: arXiv:2609.19640, "A Policy Profile for Croissant" (September 17, 2026)

+11.7µs

Added to a single decision

Measured against a decision that takes 119µs. Translate the document once and reuse it, and the addition is zero

~250×

Delivery against the decision itself

The decision costs 119µs and the process that delivers one costs 30ms. The paper works out the ratio itself and prints it

552

Decision records three carriers agreed on

Not the permit and refuse verdicts alone. The refusal class, the reasons, and every condition with its observed value were compared too

19%

Where the two authorities disagreed

35 of 184 requests. Keep one authority only and that is the share the other would have stopped, though the caller side here is a model the author wrote

1

Writing a Restriction Down, and Refusing a Request

Writing "not for commercial use" onto a data card is an old practice. Licence documents, terms of use, the restriction clauses stapled to a consent form: in the end all of them were sentences a person read and a person honoured. A machine could carry those sentences around inside a file, but it never stopped a job on the strength of one.

Croissant is the standard that moved those sentences somewhere a machine can read them. It is a dataset descriptor written as JSON-LD over schema.org, and Hugging Face, Kaggle and OpenML publish datasets in it while Google Dataset Search consumes it. Version 1.1 added a responsible-AI and governance section, which puts use restrictions in the sc:usageInfo slot. For simple conditions it points at DUO, the consent vocabulary that came out of biomedical data sharing, and for fine-grained permissions at ODRL, the W3C Recommendation. The specification states that machine-readable use restrictions can support automated compliance checking. A further thread runs alongside that one: an endpoint through which an agent discovers, downloads and loads a dataset, built on top of the same descriptor. Describing the data and driving an agent from that description now run alongside each other. Neither one refuses.

The gap the paper works in is the second half of that sentence about compliance checking. The standard never says how. A vocabulary in which a condition can be written is not a procedure by which a request is decided, and the paper splits the difference into five items.

  • No evaluation semantics. Neither the standard nor either vocabulary it recommends says what it means to check a condition against a request. Two conforming consumers may reach different verdicts on the same document without either being wrong.
  • No bound on cost. Nothing constrains what a condition may cost to evaluate. In a check that runs on every access, that turns into a performance question.
  • No failure semantics. A condition an implementation does not understand has no specified outcome. Whether the request is refused or the condition is skipped decides whether the mechanism is a gate or a suggestion.
  • No record. A check that leaves no trace of which conditions it evaluated, with the values it observed, cannot be audited after the fact and cannot be re-decided against a later policy.
  • No composition. A dataset-side condition and a caller-side authority constrain the same request, and the standard does not address the relationship.

The paper's claim is not that Croissant cannot express policy. It is that expressing policy and admitting a request are different problems. The properties an auditor needs are won or lost in the second. That is why the title calls refusal a property of the dataset.

2

A Decision Language Closed at Five Operators

The proposed profile hangs a single policy node off Croissant's dataset node. The policy carries the dataset's lifecycle state. A failClosed field is mandatory, meaning that anything unevaluable is refused. It also carries one or more actions, each naming the states that admit it and the list of conditions attached to it. Conditions may use five operators, and those five are the whole language.

No new engine — reinterpreting the paper's Figure 1 Croissant dataset + cpol: policy Translator Native gate (already existed) Decision + receipt names both authorities MCP tool schema advertises, never enforces Stripping the policy still leaves a valid Croissant document Caller policy same gate evaluates it precedence rule Caller side is modelled — not measured The upper branch is the schema advertised to an agent; the lower branch is caller policy. Neither one decides — the gate that already existed does. This carries over the structure the paper draws as Figure 1.
▲ Pebblous original diagram (reinterpreting Fig. 1) — source: arXiv:2609.19640
Operator Decision, observed o against operand e ODRL JSON Schema
min o ≥ e. Refuse if either is non-numeric odrl:gteq minimum
max o ≤ e. Refuse if either is non-numeric odrl:lteq maximum
in o equals a member of the array e odrl:isAnyOf enum
equals o = e odrl:eq const
present Whether a value exists equals the boolean e minted, none existed required, negated

Table 1 of the paper, carried over. Four of the five are operators ODRL already had, and only the presence test is minted, because ODRL defines no existence operator. A design meant to beat ODRL on expressiveness would not look like this table.

The table splits a single kind of mistake in two. The operand is written in the document in advance and the observation arrives with the request, and they fail differently. An observation of the wrong type is an ordinary condition violation and is refused right there. An operand of the wrong type is a defect in the policy itself, and it is refused at the translation step before any comparison is attempted. The profile also fixes how values compare. Numbers compare by value, so 1 and 1.0 are equal, and a boolean is equal only to a boolean, so true is not 1.

Closing the set at five is where this design earns its keep. Every condition carries an explicitly specified decision procedure, so a decision record can state in one line what was compared to what, and the cost of a whole decision is linear in the number of conditions checked. The paper declines to claim constant time. An auditor is not asking for a promise that decisions are always fast. The document alone is enough to count the work a decision takes. A sixth operator slipped into a document turns permit into refuse, never into an unchecked pass.

The last column of the table comes along with the closure. Each of the five operators has an image in JSON Schema. Each action projects onto one MCP tool that an agent can call. The tool's input schema gets one property per condition, with min becoming minimum and in becoming an enum. A caller reads the constraint before calling instead of discovering it in a refusal. The advertised schema and the enforced condition are generated from the same node, so changing a threshold in the document changes both in one edit, and the class of drift that comes from a human copying a number out of one artifact and into another disappears. The projection never replaces the check. Lifecycle state is a property of the dataset and the schema cannot carry it, so a request satisfying every advertised constraint is still refused when the state is wrong.

This profile builds no new decision engine. It translates a document into the native descriptor model of a gate that already exists and calls that gate. The semantics belongs to the profile and the engine is borrowed. Every defect, an unknown operator, a missing field, a duplicate condition name, a policy that does not declare failClosed, is mapped onto the one condition that gate already knows how to refuse on. A translation that fails produces a refusal rather than a gap.

That leaves the obvious question of why this is not simply an ODRL profile. The paper declines to take it as a fight about expressiveness. ODRL has a profile mechanism, the author writes, and it is more than adequate for everything expressed here. The objection sits elsewhere. Minting an operator in a profile gives it an IRI and a definition in prose, and it does not give it a decision procedure, because the standard specifies none. Two independent research groups supplying two different evaluation semantics for the same standard is the evidence for that. One translates ODRL into Answer Set Programming so compliance can be checked against regulatory requirements, and the other, ODRE, attaches executable functions to constraints so a policy can be enforced, not just read. Where the standard leaves a hole is exactly where those two results landed.

One condition is nailed into the design: removing every policy term must leave a valid Croissant document, and no core term may be redefined. A consumer that does not know the profile reads the document, loses the policy and keeps everything else. This layers onto the standard instead of replacing it, in the same lineage as DCAT-AP over DCAT.

3

Once on a Real Pipeline, Once on Generated Documents

The evaluation comes in two corpora, and the reason for keeping them apart is stated before either one. One is three descriptors that gated a pipeline that actually ran. That is the evidence the mechanism works in front of real execution, and every timing figure in this article comes from it. The other is a set of documents generated from the profile's grammar. That is the evidence the profile covers every feature the specification defines, and it carries no timing at all. Three real descriptors are thin evidence about a feature space, and generated documents are no evidence about production reality.

3.1A Gate in Front of the Work

The target is nf-core/demo v1.0.1. It is a three-stage bioinformatics pipeline that runs quality control on genomic sequence reads, trims them and produces a report, and it runs on public test data rather than on a fixture written for this paper. The gate hooks into the step Nextflow places before each task. A non-zero exit stops that task before its script runs. This is a block at execution time rather than an audit that reads the logs afterward.

The comparison method is the core of this part. The request matrix is generated from the descriptor rather than listed by hand: the satisfying context, the empty context, a context of irrelevant keys, one violation per condition, a non-numeric value for each numeric condition, and two undeclared action names. Each request is decided twice, once from the native descriptor and once from the emitted profile document. What gets compared is not the permit or refuse verdict but the complete decision record. Two evaluators that agree on the verdict and disagree on the refusal class, the reasons, or the conditions they claim to have checked are not equivalent in any sense an auditor would accept. All records matched.

A caveat travels with that result. Both sides of the comparison were decided by the same native gate, and the paper says that was deliberate, since a second evaluator would let a disagreement come from the evaluators rather than from the documents. So what this experiment establishes is the fidelity of the Croissant-to-native translation, and whether the gate's own authorization semantics are correct falls outside the paper.

3.257 Documents Built to Reach Every Corner of the Spec

The second corpus is built to reach everything the specification names. Five operators, three refusal classes, five conformance clauses, two carriers, and a handful of structural forms. It holds 22 valid cases and 13 defect cases, and since a valid case is stored in both carriers, 22 cases come to 44 documents, which with the 13 defect fixtures makes 57 in total. The paper nails down that these numbers are reproducibility information rather than a result. A larger corpus may exercise fewer specification features than a smaller one built to exercise them all, and only the size is visible from the size.

The three-way agreement is a claim about the valid cases only. Each case is written three ways: as the native descriptor, as the profile's own terms, and as an ODRL policy in the sc:usageInfo slot Croissant 1.1 designates for use conditions. Each of the three is run against its generated request matrix. That is 184 requests per representation and 552 complete decision records, all matching three ways. The outcome mix is recorded alongside: 20 permits, 83 condition violations, 44 undeclared actions and 37 state refusals. Agreement was not established over a pile of refusals.

In a comparison like this one, the first thing to establish is that the test can fail at all. Flipping a single row of the operator table, mapping min to odrl:lteq, broke nineteen of the comparisons. The two documents are required to be identical outside the policy node, so whatever difference remains comes from the carrier alone. The defect cases have their own record. Every defect case refuses every request put to it, 91 requests through the profile's own carrier across all 13, and 77 through the ODRL carrier across the 11 that have an ODRL form. The design in which a failed translation yields a refusal was checked document by document.

What this corpus bought is five defects, none of them reachable from three well-formed descriptors. Two of the five look fine in both the document and the code.

  • A policy that wrote a string where an array belonged fell through to substring matching. A document with "illumina" written into an in condition silently permitted "illu". Forgetting the array brackets is the obvious authoring slip, and no validator complained about the document.
  • In Python a boolean is a subclass of integer, so true compared equal to 1. A policy requiring equals: true was satisfied by an observed 1, and a condition allowing 1 or 2 admitted true. The code looked right, which is why this one took longest to see.

Both defects admit more than the policy's author wrote, and the author calls that the failure this profile exists to prevent. The sentence that follows is that writing down a decision procedure turns out to be a good way to discover that the implementation has a different one. The other repairs went the same way: a numeric operand written into an in condition crashed the evaluator instead of being refused by it, the ODRL carrier raised an exception where it should have refused, and a document that never claimed the profile was being decided anyway. Replaying the deployment corpus under the corrected comparison altered none of its 46 decisions, because the three real descriptors carry no boolean conditions.

Writing the conformance checks as SHACL shapes turned up a defect in Croissant itself. Croissant's context does not declare conformsTo as an IRI and sets a global language tag, so the values of the one property whose purpose is to identify a profile expand to language-tagged string literals rather than to IRIs. A conforming document ends up claiming conformance to the text of the address rather than to the profile that address names. The author reported this to MLCommons as issue 1047, and records that it was still open with no response from the project as of September 16, while the paper was being written.

4

The Decision Is Microseconds, Delivering It Is Milliseconds

Cost was measured over the three descriptors, 5,000 iterations per case, in-process. In the warm regime, where the document is translated once and the descriptor reused, the native descriptor and the profile document both come to 1.8µs, and the addition is zero because it is the same function on the same object. In the cold regime, where the document is read and translated per decision, 8.5µs becomes 20.2µs, an addition of 11.7µs. Most of that addition is not the profile either. JSON parsing accounts for about 9.7 of the 11.7µs, and the profile-specific translation is about 2µs. The Croissant document with its context runs to 3.1 KB against the native descriptor's 754 B.

That is where the abstract's figures stop, and an 11.7µs addition to a 119µs decision reads as nearly free. The paper blocks that reading in the very next passage. The same harness answers the question directly, the author writes, and the answer is not the comfortable one.

Replicating the gated and ungated arms 30 times each and pairing by replicate, the per-task end-to-end cost of gating resolves at 25.7 milliseconds, with a 95% confidence interval of 3.3 to 48.2 milliseconds. The increase is not the policy. It is one Python interpreter spawned per task, measured separately at 30.2 milliseconds against 9.6 milliseconds for an interpreter that does nothing at all.

Two kinds of time from the same gate Top scale: 0 to 140 microseconds 0 140µs One decision 119µs With the profile +11.7µs Bottom scale: not drawable on the axis above Gate spawned per task 30.2ms, about 250× the bars above The top two bars time the decision function. The bottom one times the process that delivers it to a task. On one scale the bottom bar runs off the page. The paper prints that ratio in the text rather than hiding it.
▲ Pebblous original diagram. Figures from arXiv:2609.19640, Section 4.2, C3

So the honest statement the paper offers is a ratio rather than an absence. The decision costs 119µs and the mechanism delivering it costs 30 milliseconds, a factor of roughly 250. Expressing policy as a standard descriptor is therefore invisible inside a per-task cost dominated by process startup, and the author adds that it would remain small against a resident gate that removed the startup entirely.

The most striking thing in this section is not a number but a withdrawal. An earlier single-run version of this measurement reported the overhead as lying below the resolution of the engine's own trace. At n=30 that is not true, and the author writes that the claim is withdrawn here, not repeated. Given that recording what was checked and how is itself the paper's subject, this passage reads as a demonstration of the method.

5

The Data Can Refuse, but It Does Not Know Who Is Asking

Reading "the data decides its own conditions" as "the data knows who may access it" gets the paper backwards. This profile has no identity model at all. Who made the request and whether that requester is entitled to anything falls outside the scope, as Section 6 of the paper states. Deciding the requester is the job of the side that binds policy to the caller, whether that is an agent control plane checking an action against approved policy before execution or a resource access control language.

The paper states the relationship between the two sides as an observation, not an opinion. Neither permit set contains the other, because the two authorities range over partially non-overlapping state spaces. Whether a dataset has passed quality control, is pending, or has been retracted is a property of the data at a moment in time and is not derivable from caller identity, so no caller-side policy can predicate on it. Whether a caller's attestation has lapsed is a property of the caller and is not derivable from a descriptor. Two authorities that each express a class of rule the other cannot must, if any such rule is written, admit requests the other refuses.

After the proof comes a witness. Four caller scopes, three datasets and the generated request matrix multiply out to 184 decisions. Both sides permitted 12, both refused 137, only the dataset refused 23, and only the caller refused 12. The 35 that disagree are 19% of the whole.

How two authorities decided the same 184 requests Both refuse 137 · 74.5% Only the dataset refuses 23 · 12.5% Only the caller refuses 12 · 6.5% Both permit 12 · 6.5% The 35 in orange disagree, what one side alone lets through Of the 23 the dataset alone refused, 3 turn on its lifecycle state, which no caller-side policy can express. Of the 12 the caller alone refused, 6 turn on lapsed assurance and 4 on a missing entitlement, which no descriptor can express.
▲ Pebblous original diagram. Figures from arXiv:2609.19640, Section 5.4

The structure matters more than the totals. Of the 23 the dataset alone refused, 20 violate a condition the caller never carried and 3 turn on the dataset's lifecycle state, which is a class of rule no caller-side policy can express. The 12 the caller alone refused are the mirror image. Six turn on the caller's own lapsed assurance and four on an entitlement it does not hold, and a descriptor can express neither.

What these numbers are not is stated twice in the paper. The request matrix is deliberately weighted toward violations, so the 19% should not be read as a disagreement rate in production traffic. And the four caller scopes are a model the author wrote after reading product descriptions, so a different set produces different counts, which the author says first. No choice of scopes produces a caller policy that predicates on dataset lifecycle state or a descriptor that predicates on caller assurance. This is also the gap the paper calls its largest: replacing the modelled caller side with an actual product and measuring again.

Running both authorities costs almost nothing. With both descriptors resident, the conjunction is 5.7µs against 2.5µs for either one alone, and re-translating both documents per decision costs 17.6µs. In exchange, a single record comes out. The joint receipt carries both verdicts, both refusal classes, both reason lists and both condition lists, including the passing conditions of the authority that permitted. The record also carries the verdict each of the five precedence rules would have yielded on the same request, written in at decision time. A later reader determines from the record alone, without either policy document in hand, whether the outcome depended on the policies or on the precedence rule in force.

6

Why Pebblous Is Watching This Paper

The question Pebblous has held onto for a long time is where a value came from and what it passed through to become what it is now. Conditions of use sit at the opposite end of that question. Where the data came from looks backward, and what it may be used for looks forward. Both are questions the data itself should be able to answer.

Narrow AI-Ready Data down to clean data and this paper reads as somebody else's business. Under a definition where ready data means no missing values, even labels and a matching schema, a sentence saying "do not use this here" is administrative paperwork outside the data. That boundary collapses once agents start finding, downloading and running datasets on their own, because the person who would have read the condition is no longer in the middle. The narrow thing this paper proves is exactly that point. A dataset that carries its own use conditions in a standard descriptor lets a gate decide from that document alone, and it gains nothing beyond that.

What we looked at longest in this structure is the record more than the decision itself. Once every decision retains which conditions were checked and what values were observed, that record becomes an asset that can be re-decided against a later policy. The paper ships a tool that replays a stored decision against a new policy without the original data, the original pipeline or the evaluator that produced it, and reports the fraction of an archive that cannot be decided, a number that would otherwise be a guess. Anyone who works on data quality diagnostics will recognise the shape. It is the difference between keeping a score and keeping the checks the score came from. The first gets thrown away when the criteria change, and the second gets recomputed.

The withdrawal in Section 4 is not the only one. An earlier version of the paper recorded the profile's identifier as not yet resolvable, and after the registration landed the author verified the redirect end to end and updated the sentence. Writing the conformance tests changed a sentence of the specification too, once it turned out that a document stripped of its policy terms could still claim to conform to the profile. The demand that every decision record what it checked is a rule the author also applied to their own manuscript.

A team that wants to hold its own organisation to the idea of data that states and proves its conditions has four things worth checking.

  • Where are our datasets' use restrictions written today? If they live only in contracts and a wiki, moving them somewhere a machine reads is the first step.
  • Is the procedure for deciding those restrictions written down in prose? With implementation code and no written procedure, nobody notices when the implementation follows a different rule. That is how three of the defects in this paper surfaced.
  • When our system meets a condition it cannot evaluate, does it block or skip? That choice separates a gate from a suggestion.
  • Does a refusal record what was checked? Without it, a refusal is indistinguishable from a bug later on.

One thing is worth saying plainly. This is a preprint that has not been peer reviewed, the evidence from real execution is three descriptors, and half the composition result is a model. The author writes all of that down. Pulling a conclusion out of it about data governance having entered its automated era would be overreach, not a reading. What we took from it is smaller. It is a record of one person building a minimal version of what has to exist, past the point where there is a slot to write a condition in, before that sentence actually stops a request, and measuring it.

Thanks for reading this far. The figures and quotations this article cites can be checked by anyone in the original at arXiv:2609.19640. We would be glad to hear what form your organisation keeps its data use conditions in, and where the place that actually checks them sits.

R

References

Academic Papers

Standards & Specifications

Reference Implementations

  • 5.doytsujin. "ok-nfcore-admission-gate." GitHub. An unreviewed repository from the author's circle — revalidates the gate's deployment cost.
  • 6.doytsujin. "ok-croissant-policy-profile." GitHub. An unreviewed repository from the author's circle — reference implementation of the policy profile.