"Success" Means It Ran: The Trust Contract Around a ShEx Conversion
There is a sentence in our own transform documentation that we treat as a design constraint rather than a caveat: **a successful response does not mean nothing changed — it means it ran.**
"Success" Means It Ran: The Trust Contract Around a ShEx Conversion
There is a sentence in our own transform documentation that we treat as a design constraint rather than a caveat: a successful response does not mean nothing changed — it means it ran.
Most schema tooling implies the opposite: a converter takes a document, returns a document, reports success — and whatever fell out in the middle surfaces later as somebody else's incident. When we implemented ShEx, the requirement was that a steward must be able to answer what will this do to our model before anything is written, and what did that cost after anything is read. Five commitments make that answerable.
1. Writing is an accountable act; reading is not
Reading a governed project as ShEx requires Viewer, changes nothing, and runs as often as you like. Writing shapes into a project — turning them into Types, Elements, and Taxonomies — requires Admin, and travels through the same governed layer every ordinary edit uses; no imported file bypasses the rules a hand edit obeys. The asymmetry is deliberate: projections should be cheap, and changing what your data means should be attributable to a named person.
2. Import is additive, and re-import upserts
The fear behind the steward's question is usually duplication: import the revised shapes and end up with two Persons, three email elements, and a model nobody can untangle. That is not the behavior. Node writes are upserts by identifier; the encoder snapshots existing relations before writing, so an edge is never written twice; mapping annotations use deterministic identifiers, so one mapping per node, standard, and URI is maintained rather than accumulated. Re-encoding into a project that already holds part of the schema upserts instead of duplicating — and nothing happens until someone posts to it.
3. Meaning changes go through a plan a human can read
Import and export are the straightforward cases. The sharper question arrives with mapping — aligning a partner's shapes onto a model you already govern. Our mapping surface is dry-run first by construction. The stateless mapping call writes nothing; every invocation is a rehearsal returning the produced schema, the loss report, and the executed plan as a machine-readable artifact naming what was matched, transformed, and dropped. That plan is the review object: store it, attach it to a change request, replay it against the same source for the same output. Where mapped results are written into a project, a dry run comes first and applying requires Admin — review, then deploy, enforced by roles rather than convention.
The gate does not bend for machines. Our engine can have a model propose a plan, and that proposal passes the identical gate a hand-authored one passes, with at most one repair attempt — a rejected repair is a rejection, not a negotiation. Any confidence the proposer reports about itself is advisory, never evidence. And because that path sends schema content to an external model provider server-side, it requires Editor or Admin membership and is declared as an outside-reaching operation rather than quietly performed.
The coder itself deliberately knows nothing about mapping languages: translating a format and reinterpreting meaning are separate jobs, and keeping them separate is what makes the second reviewable.
4. Every degradation is named, located, and explained
Moving between a graph-shape language and record-shaped formats loses things; honesty means being specific about which, where, and why. Every response carries a lossiness ledger: records with a kind, an exact path, a plain-English explanation. Four kinds cover the ground — something had no home in the target and was left out; a type was represented by a close-but-inexact neighbor; a rule such as required or an allowed-value list was relaxed because the target could not enforce it; meaning was narrowed or guessed.
ShEx exercises all four. Coming in, a shape declaring several parents is narrowed to one, recorded as a semantic narrowing naming the shape and its parent count; a value constraint we have no primitive for is approximated as a string, recorded against that element. Going out to a relational target, a governed value set becomes a text column, and the response says at that path that the allowed-value constraint is not enforced.
None of those are bugs, and none are hidden. An empty ledger is a clean conversion; a non-empty one is a review checklist that arrives with the result rather than the incident.
5. Approximate in the middle, exact at the edges
Under the ledger sits a quieter promise, one that matters to anyone sending an artifact back to the organization it came from. On import we preserve the prefix map, the exact predicate of every triple constraint, and the original xsd: datatype token of every value — including tokens we approximate internally. A date and a date-time land on the same primitive in the middle, and each still remembers which it was, so the export re-emits what you sent rather than what we inferred.
The boundary of the modeled subset belongs in the same breath, because an unpublished boundary is a trap. The coder covers the shape and triple-constraint core: datatypes, shape references, value sets, cardinalities including numeric ranges, inheritance. Prologue statements, start declarations, and qualifiers like CLOSED and EXTRA are skipped rather than modeled; an empty document is refused with a clear message.
Drift stops being anxiety and becomes evidence
Cheap, deterministic export changes the character of drift. Regenerating the projection is a routine read, encoding the same model twice yields the same document, and preserved prefixes, predicates, and datatype tokens mean a regenerated document differs from its predecessor only where the model does.
So commit the export and regenerate after every governed change. The diff history becomes a reviewable record of how meaning evolved, each hunk traceable to a deliberate, role-gated act. When a partner asks what changed since March, the answer is a diff rather than a meeting, and a disagreement between their copy and yours becomes a file you can read.
The contract in one breath
Nothing writes without the role. Re-import upserts rather than duplicates. Nothing maps without a replayable plan, and the gate treats a machine proposal exactly like a human one. Nothing degrades without a named, located, explained record. Nothing is claimed as preserved that we cannot re-emit — the difference between a translator you can put in front of an auditor and one you can only hope about.
Run a ShEx import or export against a sandbox project to see the ledger: the fine print arrives in the same response as the result. Both directions are covered in the quickstarts at https://coremodels.example.com.