Guarantees#
What the infrastructure promises, and the interfaces you can build against — three contracts on one page, each for a different reader. Building a platform or registry on BattINFO: start with the infrastructure contract. Integrating validation into a pipeline: the validation contract. Feeding bulk data in from a folder of files: the ingest manifest contract.
The infrastructure contract#
This page is for the person deciding whether to build on BattINFO: what the guarantees are, where they are enforced, and what breaks loudly instead of silently. Everything here is backed by tests or CI gates in this repo or the registry.
Records are versioned#
Every canonical record carries schema_version (currently 0.2.0, a single
module-level constant). The registry’s publish gate validates against pinned,
vendored copies of the same schemas and flags unknown versions rather than
guessing. Changes to record shape are CHANGELOG entries, never silent.
Three versions exist in the wild, and consumers should accept all three:
|
What it means |
|---|---|
|
The original record shape; records published before the 0.2.0 consolidation still carry it |
|
An interim stamp used briefly before the numbering was consolidated |
|
The current shape: snake_case keys throughout, |
The differences are additive/renaming only — no field changed meaning. New records always stamp the current version; old records validate against the same schemas (the keys they use are all still accepted).
Records are attributable#
Every emitted record’s provenance block carries battinfo_version — the
library build that wrote it. A malformed record found in a corpus two years
from now is forensically traceable to its producer. Explicitly set values are
preserved, so re-serialising another build’s record never falsifies origin.
Identifiers are deterministic#
Records saved without an explicit uid mint their IRI from the record’s
natural identity key (for a cell spec: manufacturer :: model :: format ::
chemistry :: size code), using the same seeds in every authoring path.
Consequences you can rely on:
re-running an identical ingest is a no-op, never a duplicate corpus;
the workspace and
save_*paths mint identical IRIs for identical identities (tested);records with no distinguishing identity still mint randomly — two anonymous but physically distinct cells never silently merge.
Governance of the IRI space itself is written down in the identifier policy.
One schema contract, three consumers#
The canonical JSON Schemas in src/battinfo/data/schemas/ are enforced in
three places, each with a CI gate against drift:
Consumer |
Mechanism |
Drift gate |
|---|---|---|
|
jsonschema at save/publish |
the test suite |
Registry publish gate |
vendored schemas, fails closed on unknown record types |
|
vendored schemas compiled with Ajv |
web CI: sync check + 103-record agreement corpus |
The same record gets the same structural verdict in all three.
Bulk operations are safe to automate#
bulk_save_sessioningests ~400 records/s; a 10k-record ingest takes under half a minute and is re-runnable without duplicates.ws.submit()journals every outcome and resumes interrupted batches, re-sending only what is missing; transient registry failures retry with exponential backoff.Submission content conflicts return a structured 409 naming the existing record, not a bare string.
Deprecations are announced#
Public API removals go through one release of DeprecationWarning naming the
replacement — never straight to ImportError. The policy lives in
CONTRIBUTING;
expiring shims are swept at each release.
Validation contract#
This document defines the validation behavior that consumers can rely on for the supported BattINFO scope.
Supported Entry Points#
Core validation entry points:
battinfo validatebattinfo save ... --validatebattinfo publish ... --validatebattinfo index build --validatebattinfo.validate.validate_json(...)battinfo.validate.validate_record(...)battinfo.validate.validate_publication(...)
Policy Names#
BattINFO currently exposes these named validation policies:
defaultstrictpublisheringest
Policy semantics:
default: schema and references are errors; semantic issues default to warnings.strict: schema, references, and semantic issues are errors.publisher: publisher-oriented validation with semantic and publication checks as errors.ingest: schema remains strict while semantic and reference issues can remain warnings during staged cleanup.
Issue Model#
Structured validation issues expose these fields:
codeseveritypathmessagehintvalidatorresource_typeprofile
Severity values:
errorwarning
Representative issue codes:
schema.requiredschema.format.urischema.format.date_timeschema.profile_unknownreference.missingreference.type_mismatchsemantic.short_id_mismatchsemantic.temporal_order_invalidsemantic.electrode_holders_mixed(withsemantic.electrode_role_expected/semantic.electrode_polarity_expected: the electrode holders disagree with the cell configuration — see Electrodes)publication.distribution_url_invalidpublication.jsonld_parse_error
CLI Contract#
battinfo validate supports:
--format text--format json
Text mode:
emits a human-readable pass/fail summary
includes warnings when validation succeeds with non-fatal issues
JSON mode emits a machine-readable payload with:
okmodepolicyprofilesource_rootissue_counterror_countwarning_counterrorsissues
Example issue object:
{
"code": "schema.format.uri",
"severity": "error",
"path": "dataset.url",
"message": "'not-a-uri' is not a 'uri'",
"hint": null,
"validator": "format",
"resource_type": "dataset",
"profile": null
}
Exit codes:
0when no error-severity issues are present1when one or more error-severity issues are present
Current Boundary#
Reference validation is supported against repository-style source trees through source_root.
Every *_id field on a record body resolves to a record of one expected type, and a reference that resolves to the wrong type is reference.type_mismatch rather than a pass. The distinctions the checks enforce are meaningful ones: cell_instance.cell_spec_id must be a cell-spec, while cell_instance.working_electrode_id / counter_electrode_id must be electrode records — the built batch, not the electrode-spec design, which the cell spec already links.
This is sufficient for the core scope, but it is not yet the long-term scalability model for larger external registries or snapshots.
Ingest manifest contract#
battinfo.ingest.json is the folder-local manifest for the battinfo ingest ...
workflow.
Purpose:
declare the typed subject being ingested
point that subject to a curated reusable type record
provide the minimum publication metadata needed for repeatable ingest
configure folder scanning rules for attached files
Normative schema: assets/schemas/ingest-manifest.schema.json
Packaged runtime copy: src/battinfo/data/schemas/ingest-manifest.schema.json
Scope#
This manifest is not the source of truth for reusable type definitions.
Use it for:
one operational ingest folder
one typed instance-like resource
file discovery and publication defaults
Do not use it for:
curated
cell-specauthoringediting canonical registry output
replacing the source folder itself
Required fields#
resource_typecurrent allowed value:
cell-instance
type_recordpath to the curated reusable type record
Optional fields#
resource_iripreserved canonical IRI for the ingested resource when already assigned
resource_namehuman-facing instance label used as the default generated serial/name
workspace_idregistry workspace id used during bundle/publish
publisher_idpublisher id used during bundle/publish
source_versionsubmission version string written into generated workspace/package state
licensedefault dataset license
rulesfile discovery and filename-to-test-kind inference overrides
Rules object#
rules currently supports:
photo_globstring or string array
default:
image/photo/*.jpgimage/photo/*.jpegimage/photo/*.png
timeseries_globstring or string array
default:
timeseries/raw/*.csv
test_kind_from_filenameobject mapping lowercase filename tokens to BattINFO test kinds
default:
rate -> rate_capabilityici -> icicapacity -> capacity_check
Minimal example#
{
"resource_type": "cell-instance",
"type_record": "battinfo-records/records/cell-spec/google--g20m7--2025/record.json"
}
Practical example#
{
"resource_type": "cell-instance",
"type_record": "battinfo-records/records/cell-spec/google--g20m7--2025/record.json",
"resource_iri": "https://w3id.org/battinfo/cell/15qn-rpd4-xhy7-kx2q",
"resource_name": "google--g20m7--2025--15qnrp",
"workspace_id": "google-g20m7-instance-demo",
"publisher_id": "demo-lab",
"source_version": "2026-04-10",
"license": "CC-BY-4.0",
"rules": {
"photo_glob": [
"image/photo/*.jpg",
"image/photo/*.jpeg",
"image/photo/*.png"
],
"timeseries_glob": [
"timeseries/raw/*.csv"
],
"test_kind_from_filename": {
"rate": "rate_capability",
"ici": "ici",
"capacity": "capacity_check"
}
}
}
Operational rule#
For routine use:
users edit the ingest folder and
battinfo.ingest.jsonBattINFO generates the
Workspaceusers do not treat the generated workspace as the primary long-term source by default
Current implementation boundary#
The manifest contract is designed to expand to other typed resource kinds, but the current ingest engine only implements:
cell-instance
Additional resource_type values should only be added when the ingest engine and
its downstream record-generation logic actually support them.