Model integrity
Provenance Has to Survive Every Copy
A converter that correctly refuses to invent provenance becomes a data-degradation engine the moment it runs over data that already has some. We shipped that, and measured it before we found it.
The rule is easy to state
Every value in the model carries a basis: how it came to be that number. A stated value was given by somebody who is accountable for it. A derived value was computed from other values that are themselves traceable. An estimated value is a placeholder standing in until someone does the work. And an unattributed value is one whose origin nobody recorded.
The rule that makes the basis worth carrying is a restriction on what you may do with it. Only stated and derived values may be treated as a specification. Everything else is a working figure. A mass roll-up that silently mixes the two produces a number with a decimal point and no standing, which is worse than no number, because a number invites a decision.
The refusal that is correct
MBE3Dstudio has two halves that both hold model data, and a bridge that copies entities from one to the other. The older half predates the provenance model: its values are frequently just values, with no recorded origin at all.
So the bridge has to decide what a value with no provenance becomes on the other side. It refuses to guess. A value arriving with nothing recorded is written as unattributed, not stated, which would fabricate an accountable author, and not estimated, which would claim a derivation nobody performed.
That refusal is the whole design working as intended. Inventing a basis rebuilds, inside the new system, precisely the defect the new system exists to prevent. If you are going to carry provenance at all, the one thing you may never do is manufacture it.
Where it goes wrong
Restarting a server drops its in-memory state, so a workspace being read again is rebuilt from its snapshot and event log. That reload replaces the whole entity set, and the entity store (correctly, on its own terms) tells the bridge that the whole model changed.
Now put the two behaviours next to each other. Every entity crosses the bridge again. Every value the older half never recorded an origin for is converted again. And every one of those overwrites a value on the other side that already had a real basis, replacing it with unattributed.
Both pieces are individually right. The reload is right to replay everything. The bridge is right to refuse to invent a basis. Composed, they are a mechanism that degrades the model a little on every restart.
A worked case
We found it by polling one workspace every thirty seconds and restarting the service in the middle of the run. Nobody was editing the model, and the entity count never moved.
| Elapsed | stated | unattributed | Entities |
|---|---|---|---|
| 180 s | 149 | 9 | 26 |
| 210 s (restart) | 144 | 17 | 26 |
| 240 s | 144 | 17 | 26 |
Five values left the category the tool treats as specification, and eight arrived in the category it treats as untrustworthy, in the time it took a process to come back up. A second workspace that nobody had touched at all showed the same figures. Steady state was perfectly stable; only the restart moved anything.
The damage accumulates, because each restart converts a further tranche and there is nothing that converts them back. A model does not become wrong here. It becomes progressively less able to say which of its numbers you are allowed to rely on, which is a slower and more expensive kind of wrong.
The part that surprises people
Nothing fails. No exception is raised, no log line is written, no validation complains, and the entity count (the number anybody would watch) does not move. The model still opens, still renders, still exports.
It is also not reproducible on demand in the way a bug normally is. It fires on process restart, so it never appears while you are debugging and always appears in the gap between two sessions. From the outside, values look like they decayed on their own overnight.
The only reason it was caught is that provenance is a first-class counted property. by_basis is in the model summary, so the drift showed up as a number changing when no number should have changed. Provenance that lives in a comment or a spreadsheet column is provenance nobody can audit, and this class of bug is invisible without the audit.
The fix, and the rule behind it
A reload is not new information. It is a process catching up to what was already durable. The other side of the bridge already holds those entities, with better provenance than the reload can reconstruct, so there is nothing in a restore that the bridge needs to hear.
So the echo is muted for the duration of a restore, using the same mechanism the codebase already had for bulk operations that take responsibility for their own synchronisation. Two consecutive restarts afterwards left two workspaces byte-identical, where every previous restart had moved them.
The general rule is the more useful part. Provenance is not a property of a schema. It is a property of every path data takes through the system, and the paths that lose it are rarely the ones you designed. They are the reloads, the caches, the migrations, the imports, the synchronisation between two stores that each believe they are authoritative. Those are exactly the paths nobody reviews, because they move data rather than compute it.
What to take from it
- Count your provenance and put the count somewhere visible. A basis you cannot aggregate is a basis you cannot audit, and this failure is invisible without the aggregate.
- Audit the copies, not just the writes. Every conversion, sync and reload is a place where a basis can be silently downgraded, and none of them look like they are changing data.
- Never let a lossy direction run over a lossless one. When two stores disagree about how much they know, the one that knows less must not be the one that speaks last.
- A refusal to guess is correct and is not sufficient. Doing the right thing with unknown provenance still destroys known provenance if you do it to data that already had some.
Model-based engineering earns its keep by letting you ask what a number rests on. That guarantee is only as strong as the least careful piece of plumbing between the place a number is entered and the place it is read.
U.S. Provisional Patent App. No. 64/073,689. Patent Pending.