Status: implemented (designed 2026-07-18) · Skill: dataset
Context
Thedataset skill keeps a single, git-tracked, public registry (datasets.yml)
as the source of truth for every dataset a project depends on. The schema is
defined in the skill (../../../skills/dataset/SKILL.md, “Manifest”) and grounded
in ../../../resources/references/dataset-management-standards.md (§2 schema, §3
tiers) and the shared base-asset record in
../../../resources/substrate/asset-registry.md.
Today the manifest has no tooling. Interim (until the module is
implemented): the skill orchestrates the step manually — loading with
yaml.safe_load, eyeballing the required fields against the table, and hand-mapping
Croissant on register/export. That is error-prone and does not scale to audit
across a whole manifest, nor does it give the venue-mandated Croissant round-trip
(NeurIPS D&B now requires a Croissant file) any machine backing. Once honest-scholar
is installed (via ensure-tooling) the skill
calls honest-scholar dataset … instead.
This proposal covers only the manifest tooling: parsing, schema validation, and
Croissant/DataCite interop. Retrieval, the rclone mirror, fixity re-hashing, and the
resolution chain are a separate proposal — this tooling reads and writes
metadata and never touches bytes or the network.
Goal
A light-dependency Python module thedataset skill’s register and audit verbs
call, that:
- Loads
datasets.ymlinto typed records and reports precise, located errors. - Validates the base asset record plus tier-conditional required fields, with clear messages (which entry, which field, why).
- Ingests a published Croissant JSON-LD to bootstrap a draft entry.
- Emits a Croissant JSON-LD file per entry (or for the whole registry).
- Round-trips DataCite citation fields for citability / DOI minting.
sha256:<64-hex>), rclone, tier decisions
(human-confirmed), or choosing a dataset for a claim (agency principle).
Design sketch
Single module, stdlib +pyyaml only. No new heavy deps (no jsonschema,
no pydantic, no datasets); validation is hand-rolled against the documented
field table, Croissant I/O uses stdlib json.
Data model
@dataclass records mirroring the schema — Manifest(mirror, datasets[]),
DatasetEntry(id, version, tier, license, redistributable, access, files[], …),
FileRef(path, sha256, size), Retrieval(kind, url), Citation(...). Dataclasses
keep the surface stdlib-only and JSON/YAML round-trippable.
Validation rules
- Core (all tiers):
id(unique slug),version,tier ∈ {A,B,C},license(SPDX id or explicit terms + URL),redistributable: bool,access ∈ {open,gated},files[]each withpath+sha256matching^sha256:?[0-9a-f]{64}$,datasheet(path/URL required; a"N/A + <reason>"placeholder is accepted structurally byvalidatebut flagged byauditas a datasheet-completeness deficiency, never a clean state). - Tier-conditional:
source/retrievalrequired for Tier B (andretrieval.kind ∈ {http,doi,openml,git-lfs,manual}); acquisitioninstructionsrequired for Tier C;sensitivityrequired if PII/confidential. - Cross-entry: duplicate
iddetection; atier: Aentry withredistributable: falseoraccess: gatedis a hard error (tier follows the license/access, not convenience — matches the skill’s tier-consistency guardrail). - Errors accumulate (report all, not first-fail) and are formatted as
entry '<id>': <field>: <reason>; validation returns a structured result theauditverb renders as a gap report.
Croissant mapping (registry ⇄ JSON-LD, schema.org/Dataset)
Registry is the superset source of truth; Croissant is an export/interop
format. Ingest fills what it can and leaves tier / retrieval / datasheet /
sensitivity as
TODO for the human on register. Emit produces a valid
@context/@type: Dataset document; fields with no registry value are omitted
rather than emitted empty.
DataCite
citation is the DataCite 6-mandatory tuple (Identifier/DOI, Creator, Title,
Publisher, PublicationYear, ResourceType=Dataset). Validation warns (not errors)
when incomplete, since a citable record may not yet exist; emit maps it to
citeAs; the tuple is the minimum to mint a DOI at a public archive.
API / CLI the skill verbs call
Importable fromhonest_scholar.dataset.manifest:
honest-scholar dataset command group:
registercallsingest(if a Croissant is supplied) thenvalidateon the new entry before writing; the human still confirms tier + license.auditcallsvalidateacross every entry and folds the report into its fixity/presence/completeness output.
Dependencies & posture
- Deps:
pyyaml+ stdlibjsononly. Nojsonschema, nopydantic, no network, no crypto. Fits the plugin’s light posture (asset-registry.md, “Plugin vs. consumer”: the plugin owns the schema + loader/validator). - Read/write scope: metadata only. Never opens data files, never hashes, never reaches the network — that boundary is what keeps this proposal separable from retrieval/mirror tooling.
- Placement: ships in the plugin (generic engine); consumer projects own the
datasets.ymlentries and blobs.
Open questions
- Package vs. single file: start as one
manifest.py, or splitcroissant.py/schema.pynow? - Should base-record validation be shared with
literature(common substrate spine) or kept dataset-local until a second consumer exists? - Croissant
recordSet/ RAI metadata: emit a minimal stub, or leave to the human? - SPDX id validation — ship a small allowlist, or accept any non-empty string plus the explicit-terms+URL alternative?
- DataCite: v4.6 (anchored in the standards digest) vs. v4.7 — does emit need to declare a schema version?
Acceptance criteria
loadparses a validdatasets.ymlinto typed records and raises with the offending line/entry on malformed YAML.validateflags every missing/invalid core field and every tier-conditional omission (B: source/retrieval; C: instructions; PII: sensitivity), accumulating all violations withentry '<id>': <field>: <reason>messages; exits non-zero from the CLI on any error.- Duplicate
id, and atier: Aentry withredistributable: falseoraccess: gated, are hard errors. emitproduces schema.org/Dataset Croissant JSON-LD whosedistribution[]carriescontentUrl+sha256, validated by a round-tripentry → croissant → entrythat preserves core + citation fields.ingestbootstraps a draft entry from a published Croissant and marks tier/retrieval/datasheet/sensitivity as human-TODO.- No dependency beyond
pyyaml+ stdlib; no network or filesystem access to data files during validate/emit/ingest. registerandauditinvokehonest-scholar dataset …; the skill’s § Tooling note already documents this CLI (the interim manual workaround stays until the module lands).
Links
- Skill:
../../../skills/dataset/SKILL.md(Manifest, Tiers; § Tooling) - Standards digest:
../../../resources/references/dataset-management-standards.md(§2, §3) - Substrate base record:
../../../resources/substrate/asset-registry.md - Design:
../03-dataset.md,../04-substrate-and-contract.md - ADRs:
../../../decisions/0009-dataset-thin-manifest.md,0012-shared-substrate.md - Separate proposal (not this one): retrieval / rclone mirror / fixity tooling