The normative Rules: list in spec.md §4.6 Imports does not mention that symlinked import paths are rejected, even though rejection is both documented elsewhere and enforced in code.
The gap
spec.md §4.6 Imports (spec.md:309) ends with a Rules: list at spec.md:338-345:
- Relative paths only (no bare module names)
as alias namespaces all exports: access via {{alias.name}}
- Without alias (merge): exports enter current scope (name collision → compilation error)
- Selective: only listed names are brought into scope
- Circular imports → compilation error
- Import resolution is recursive (imports can import)
Nothing about symlinks.
What actually happens
Symlinked import paths are refused. This is:
- Documented —
SECURITY.md:37-39: "Symlink rejection: symlinked import paths are refused. Resolution is written to be TOCTOU-safe (the resolved target is validated, not just the pre-resolution path)."
- Enforced —
NativeFs::check_symlink at crates/mds-core/src/fs.rs:343, reached from the import-resolution path and applied by every surface that reads a file.
So a reader working from the normative spec alone would conclude that @import "./link.mds", where link.mds is a symlink, resolves like any other relative import. It does not — it errors.
Why this matters
This is spec/implementation drift on a security control, which is the category where drift is least acceptable. spec.md is the normative document; a security guarantee that appears only in SECURITY.md is documented but not specified. Anyone implementing MDS from the spec — or writing conformance tests against it — would omit the control and produce something that looks conformant while being materially less safe.
The editorial call
The §4.6 Rules list is otherwise purely semantic — naming, scoping, collisions, cycles, recursion. Filesystem-level constraints are a different category, and there are two reasonable placements:
- Add symlink rejection (and possibly the sibling controls —
.. traversal rejection, NUL-byte rejection, non-UTF-8 path rejection, all listed at SECURITY.md:33-43) to the §4.6 Rules list, so the import contract is complete in one place.
- Add a dedicated normative security/filesystem section to
spec.md and cross-reference it from §4.6, keeping §4.6 semantic.
Option 2 scales better if the other fs.rs controls are specified too. Either way the current state — normatively silent — is the one option that should not persist.
Why deferred
Found during design work and deliberately scoped out of the current change, which is not touching spec.md. Filing so the drift is tracked rather than rediscovered.
The normative Rules: list in
spec.md§4.6 Imports does not mention that symlinked import paths are rejected, even though rejection is both documented elsewhere and enforced in code.The gap
spec.md§4.6 Imports (spec.md:309) ends with a Rules: list atspec.md:338-345:as aliasnamespaces all exports: access via{{alias.name}}Nothing about symlinks.
What actually happens
Symlinked import paths are refused. This is:
SECURITY.md:37-39: "Symlink rejection: symlinked import paths are refused. Resolution is written to be TOCTOU-safe (the resolved target is validated, not just the pre-resolution path)."NativeFs::check_symlinkatcrates/mds-core/src/fs.rs:343, reached from the import-resolution path and applied by every surface that reads a file.So a reader working from the normative spec alone would conclude that
@import "./link.mds", wherelink.mdsis a symlink, resolves like any other relative import. It does not — it errors.Why this matters
This is spec/implementation drift on a security control, which is the category where drift is least acceptable.
spec.mdis the normative document; a security guarantee that appears only inSECURITY.mdis documented but not specified. Anyone implementing MDS from the spec — or writing conformance tests against it — would omit the control and produce something that looks conformant while being materially less safe.The editorial call
The §4.6 Rules list is otherwise purely semantic — naming, scoping, collisions, cycles, recursion. Filesystem-level constraints are a different category, and there are two reasonable placements:
..traversal rejection, NUL-byte rejection, non-UTF-8 path rejection, all listed atSECURITY.md:33-43) to the §4.6 Rules list, so the import contract is complete in one place.spec.mdand cross-reference it from §4.6, keeping §4.6 semantic.Option 2 scales better if the other
fs.rscontrols are specified too. Either way the current state — normatively silent — is the one option that should not persist.Why deferred
Found during design work and deliberately scoped out of the current change, which is not touching
spec.md. Filing so the drift is tracked rather than rediscovered.