magus v0.3.0 is out. See what's new
¶ View markdown source · ✎ Suggest an edit
2 min read

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.yaml with a required reason:

    # magus.yaml
    spells:
      allow_shadow:
        - name: spells/hello
          reason: web/studio pins a patched hello until the upstream fix lands
    

    The reason is mandatory, so the intent stays auditable. magus doctor flags an allow_shadow entry 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 an mgs_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.

See also

  • workspace: how spell imports resolve, root-wins, across a nested workspace.
  • spells: authoring a spell and binding it to a project.
  • magus doctor: flags an allow_shadow acknowledgment whose shadow no longer exists.
MGS1002magusfilespellsimportsshadowwardworkspace
Last updated (b022d75e)
Glossary

Workspace

The magus root directory that owns a set of projects and shared config; the unit magus operates over. See workspace.

Project

A directory magus recognizes as a unit of work (it has a magusfile); the unit of caching, scheduling, and dependency tracking. See workspace.

Magusfile

The magusfile.buzz that declares a project's targets (as export funs) and binds its spells. See targets.

Spell

A language/runtime adapter (e.g. go, md) that maps generic targets onto a toolchain's real commands. See spells.

Charm

An execution modifier attached with : (lint:rw) that changes how a target runs, not which one; the built-in rw flips a check-only target to mutate in place, and ci always strips it. See charms.

Ward

A coded diagnostic that inspects a resolved op and nudges or blocks an anti-pattern before it runs. See wards.

Buzz

The language magusfiles are written in (the .buzz engine). See engines.

Affected

The set of projects touched by a change; magus affected <target> runs a target only over them. See affected.

Service

A long-running or shared process magus manages across runs, distinct from a one-shot target. See services.

Conventions

Placeholders

Angle brackets mark a value you replace with your own - never type the brackets:

magus run <target>
magus completion <shell>    # e.g. bash, zsh, fish

<target>, <path>, <shell>, <name> and the like are stand-ins, not literal text.