MGS1002: spell import shadowed
A workspace defines the same spell import (spells/<name>) at two levels where one
directory is an ancestor of the other. Spell imports resolve root-wins: the
copy nearest the workspace root is canonical, so the deeper copy is never loaded.
The deeper definition is dead code, and this ward blocks the run until you resolve
or acknowledge it.
[MGS1002] spell import "spells/hello" is defined at web/studio/spells/hello/spell.buzz
but shadowed by spells/hello/spell.buzz: imports resolve root-wins, so the deeper
spell is dead. Move or rename it, or acknowledge the shadow in magus.yaml
(spells.allow_shadow) with a reason.
see: .../MGS1002.md
Why
A local spell is imported by a path-style name, import "spells/hello". magus
resolves it by walking a spells/ directory at every level from the workspace root
down to the importing magusfile, and the root-most match wins (see
the workspace model). That rule is deliberate: a spell name
means one thing across the workspace, the same way a charm name does.
The consequence is that a spells/hello placed next to a nested project, when a
spells/hello also exists higher up, can never be reached. An author who put it
there expected it to be used; instead the ancestor silently wins. That is a
footgun, not a self-contradiction, so unlike the kind-coherence wards
(MGS5002, MGS5003) it can be
acknowledged rather than only fixed.
Sibling subtrees are not affected: web/spells/hello and api/spells/hello are
not a shadow, because no single project's root-to-leaf path sees both. Only an
ancestor-and-descendant pair triggers this code.
Resolution
Pick one:
-
Rename the deeper spell so it no longer collides (
spells/hello-web). Its import name changes, but it is now reachable. -
Move the shared spell to the level that should own it. If every project should get the deeper behavior, promote it to the workspace root and drop the ancestor copy.
-
Acknowledge the shadow when it is deliberate (a nested project pins a patched copy on purpose). List its import path in
magus.yamlwith a required reason:# magus.yaml spells: allow_shadow: - name: spells/hello reason: web/studio pins a patched hello until the upstream fix landsThe reason is mandatory, so the intent stays auditable.
magus doctorflags anallow_shadowentry whose shadow no longer exists, so stale reasons get pruned.
What this is NOT
- Not a name collision in the spell registry. This is about the import path
(
spells/hello) at two directory levels, not two spells sharing anmgs_getName. It fires from the workspace layout, before a spell's contents matter. - Not triggered by sibling reuse. Two projects in different subtrees may each
ship a
spells/hello; only an ancestor-descendant pair is a shadow.