Skip to content

The cache-file format has no owner: consumers must mirror the emitter, so every new frontmatter field is a downstream break #89

Description

@cmungall

linkml-reference-validator writes the files in references_cache/, but offers no way to validate one. There is an emitter (ReferenceFetcher._save_to_disk) and a deliberately lenient reader (_load_markdown_format, which reads field by field with frontmatter.get(...) and ignores anything it does not recognise). Both are private, and there is no model, schema, or checker for the format.

So a consumer that wants to gate on cache-file integrity — "is this file well-formed, does its name match its reference_id, has somebody hand-edited it" — has to reimplement the format. dismech did: ReferenceCacheFrontmatter, a Pydantic model with extra="forbid" listing every field it had observed this library write.

That mirror inverts your own leniency into strictness, and the result is that any field you add is a breaking change downstream. It just happened. Upgrading from v0.2.1 to current main added five fields:

extractor_version        145
xml_extraction_version    44
full_text_declined        32
full_text_access_type      3

Validating a single KB entry rewrote 146 cache files, and all 146 then failed dismech's check-reference-cache-frontmatter gate with Extra inputs are not permitted. Nothing was wrong with the files; the downstream schema simply did not know about fields the upstream emitter had started writing.

The generalisation is the problem, not this instance. Every consumer that gates on these files has to mirror your emitter, and every field you add obliges you to negotiate a migration with each of them — or they go red. That is a lot of churn for a library whose reader does not care about unknown fields at all.

What I think should happen

The library that writes the format should own its contract. Concretely:

  1. Publish the frontmatter model as public API, versioned with the library.
  2. Publish a checkervalidate_cache_file(path) and scan_cache_dir(dir) returning structured findings — so a consumer gets "is this a valid cache file" without reimplementing it.
  3. Make it forward-compatible by construction: unknown keys must be allowed. This matters in both directions. Your reader already ignores them, so strictness would be a new and stricter claim than the library itself makes. And consumers legitimately add their own — dismech writes a database: field naming the structured source (Orphanet, ClinGen, ICEES) a record came from, which your loader silently and correctly ignores today.

A validator that rejects unknown keys would break that, so the sensible contract is: these fields must be present and well-typed; anything else is none of the library's business. If you would rather extensions be namespaced (x_database:, or a nested extensions: map) that works too, as long as it is declared rather than left for each consumer to guess.

Then a downstream gate becomes: call your checker for format validity, and layer only genuinely local policy on top. dismech's remaining rules are things you should not care about — for example that a PMID: record must carry authors or a journal, with a carve-out for NCBI Bookshelf and agency-guideline monographs.

Interim

dismech has added the five fields to its own model so its gate passes, including html_full_text_version, which none of the 146 files carried but the emitter can write. That is a patch over the symptom and it will need doing again on the next field you add — which is the argument for this issue.

Related: this is the same coupling that #88 shows from the other side. A consumer cannot tell "this record genuinely has no abstract" from "this version of the extractor could not find one", because the file format carries no statement about what the writer was able to determine.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions