Description
The MDL writer's units field is a typed unit expression, not free text, but the app lets a modeler type arbitrary text into a units field, and that text survives to the datamodel. When a variable's units string is not a valid unit expression, the writer serializes it verbatim (after #849 free-text sanitization) and the exported MDL then hard-fails parse_mdl on re-import: the whole model fails to load with a units-grammar error (e.g. expected unit name, found /), rather than emitting an ExportWarning or being sanitized to a valid/empty unit expression.
Triggering inputs include:
- a bare number as units, e.g.
"3.14"
- a stray comma
- the
/ that the #849 sanitizer substitutes for a dangling | ("|" -> "/")
Relationship to #849 (this is a distinct gap)
#849's sanitizer keeps the units field structurally safe -- it never spawns phantom variables or drops the real one, verified by the passing units_free_text_is_structurally_safe proptest. But structural safety is not the same as validity: the sanitizer does not (and cannot) make arbitrary text a valid unit expression. So a units string can pass #849 and still make the exported model fail to load. This is a separate failure mode (hard load failure on re-import) from #849's silent structural corruption (phantom variables from |/~).
Why it matters
A | (or bare number) entered as units in the app survives to the datamodel, and an MDL export -> re-import round trip then fails to load the model with no actionable warning. The user gets an opaque units-grammar parse error instead of either a clean model or an ExportWarning naming the offending variable/field.
Components affected
src/simlin-engine/src/mdl/writer.rs -- sanitize_free_text (the units | -> / substitution) and the units-emission path
parse_mdl (units-expression grammar) on the re-import side
Possible approaches (policy decision for the writer owner)
- Validate the sanitized units against the unit-expression grammar in the writer; when it fails, drop to empty units and emit an
ExportWarning (consistent with the #856 warnings channel).
- Make the units-field
| substitute a space (units-insignificant) instead of /, and treat bare-number / dangling-operator units as unrepresentable-with-warning.
Note: the units | -> / choice in sanitize_free_text was made deliberately for comment-field trailing-space idempotence, so any change should be field-specific (units vs. comment), not global.
Context
Discovered while adding property-based tests for the MDL writer (branch mdl-writer-hardening, src/simlin-engine/src/mdl/writer_proptest.rs). The units_free_text_is_structurally_safe proptest confirms the #849 sanitizer keeps units structurally safe; this issue is the separate validity gap it does not cover.
Description
The MDL writer's
unitsfield is a typed unit expression, not free text, but the app lets a modeler type arbitrary text into a units field, and that text survives to thedatamodel. When a variable'sunitsstring is not a valid unit expression, the writer serializes it verbatim (after#849free-text sanitization) and the exported MDL then hard-failsparse_mdlon re-import: the whole model fails to load with a units-grammar error (e.g.expected unit name, found /), rather than emitting anExportWarningor being sanitized to a valid/empty unit expression.Triggering inputs include:
"3.14"/that the#849sanitizer substitutes for a dangling|("|"->"/")Relationship to #849 (this is a distinct gap)
#849's sanitizer keeps the units field structurally safe -- it never spawns phantom variables or drops the real one, verified by the passingunits_free_text_is_structurally_safeproptest. But structural safety is not the same as validity: the sanitizer does not (and cannot) make arbitrary text a valid unit expression. So a units string can pass#849and still make the exported model fail to load. This is a separate failure mode (hard load failure on re-import) from#849's silent structural corruption (phantom variables from|/~).Why it matters
A
|(or bare number) entered as units in the app survives to the datamodel, and an MDL export -> re-import round trip then fails to load the model with no actionable warning. The user gets an opaque units-grammar parse error instead of either a clean model or anExportWarningnaming the offending variable/field.Components affected
src/simlin-engine/src/mdl/writer.rs--sanitize_free_text(the units|->/substitution) and the units-emission pathparse_mdl(units-expression grammar) on the re-import sidePossible approaches (policy decision for the writer owner)
ExportWarning(consistent with the#856warnings channel).|substitute a space (units-insignificant) instead of/, and treat bare-number / dangling-operator units as unrepresentable-with-warning.Note: the units
|->/choice insanitize_free_textwas made deliberately for comment-field trailing-space idempotence, so any change should be field-specific (units vs. comment), not global.Context
Discovered while adding property-based tests for the MDL writer (branch
mdl-writer-hardening,src/simlin-engine/src/mdl/writer_proptest.rs). Theunits_free_text_is_structurally_safeproptest confirms the#849sanitizer keeps units structurally safe; this issue is the separate validity gap it does not cover.